liujie
2026-02-04 ed36ab45d01b9e713d64108ec0fa73be878e4380
Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/java/Robotaxi
4个文件已删除
2 文件已重命名
34个文件已添加
50个文件已修改
4101 ■■■■■ 已修改文件
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/DailyCountDTO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/MileageDurationDTO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/OrderStatisticsDTO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/StatisticsQueryDTO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/UserStatisticsDTO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/factory/account/XpAppUserClientFallbackFactory.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/factory/operation/RemoteMenuFallbackFactory.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/factory/order/XpOrderClientFallbackFactory.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/feign/operation/RemoteMenuService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/feignClient/account/XpAppUserClient.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/feignClient/operation/SysMenuClient.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/feignClient/order/XpOrderClient.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/model/account/SysRole.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/model/account/SysRoleBak.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/model/account/SysUser.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/model/operation/TreeSelect.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/model/operation/XpWhitelist.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/vo/account/LoginUser.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-common/xp-common-core/src/main/java/com/xiaopeng/common/core/web/page/BasePage.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-common/xp-common-datascope/src/main/java/com/xiaopeng/common/datascope/aspect/DataScopeAspect.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/pom.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/controller/SysRoleBakController.java 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/controller/SysRoleController.java 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/controller/SysUserController.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/controller/XpAppUserController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/mapper/SysRoleMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/mapper/SysUserMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/mapper/XpAppUserMapper.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/dto/SysRoleAddDTO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/dto/SysRoleEditDTO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/dto/UserPwdResetDTO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/query/SysRolePageQuery.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/query/SysUserListQuery.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/vo/SysMenuVO.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/vo/SysRoleDetailVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/vo/SysRolePageVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/ISysRoleService.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/ISysUserService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/IXpAppUserService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/SysLoginService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/SysPermissionServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/SysRoleServiceImpl.java 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/SysUserServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/XpAppUserServiceImpl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/java/com/xiaopeng/account/vo/GetSysUserList.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/resources/mapper/account/SysRoleMapper.xml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/resources/mapper/account/SysUserMapper.xml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-account/src/main/resources/mapper/account/XpAppUserMapper.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/controller/IndexController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/dto/IndexDataQuery.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/mapper/XpFleetMapper.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/mapper/XpVehicleMapper.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/service/IndexDataService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/service/impl/IndexDataServiceImpl.java 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/vo/IndexDataVO.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/resources/mapper/maintenance/SysMenuClient.xml 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/resources/mapper/maintenance/XpFleetMapper.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-maintenance/src/main/resources/mapper/maintenance/XpVehicleMapper.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/IndexController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/XpInvitationCodeController.java 289 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/XpWhiteListController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java 394 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/XpWhitelistOperlogController.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/dto/WhitelistAddDto.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/exportExcle/XpInvitationCodeExportExcel.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/exportExcle/XpWhitelistExportExcel.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/exportExcle/XpWhitelistLogExportExcel.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/importExcel/InvitationCodeImportExcel.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/importExcel/WhitelistImportExcel.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/mapper/XpInvitationCodeMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/mapper/XpWhitelistMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/mapper/XpWhitelistOperlogMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/query/InvitationPageListQuery.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/query/WhitelistLogPageListQuery.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/query/WhitelistPageListQuery.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/IXpInvitationCodeService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/IXpWhitelistOperlogService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/IXpWhitelistService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/impl/XpInvitationCodeServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/impl/XpWhitelistOperlogServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/impl/XpWhitelistServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/vo/IndexDataVO.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/resources/mapper/operation/XpInvitationCodeMapper.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/resources/mapper/operation/XpWhitelistMapper.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-operation/src/main/resources/mapper/operation/XpWhitelistOperlogMapper.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-order/src/main/java/com/xiaopeng/order/controller/XpOrderController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-order/src/main/java/com/xiaopeng/order/mapper/XpOrderMapper.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-order/src/main/java/com/xiaopeng/order/service/IXpOrderService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-order/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-service/xp-order/src/main/resources/mapper/order/XpOrderMapper.xml 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/DailyCountDTO.java
New file
@@ -0,0 +1,21 @@
package com.xiaopeng.api.dto.statistics;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * 每日统计数据DTO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "每日统计数据DTO")
public class DailyCountDTO {
    @ApiModelProperty(value = "日期(yyyy-MM-dd)")
    private String date;
    @ApiModelProperty(value = "数量")
    private Long count;
}
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/MileageDurationDTO.java
New file
@@ -0,0 +1,23 @@
package com.xiaopeng.api.dto.statistics;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
 * 里程时长统计DTO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "里程时长统计DTO")
public class MileageDurationDTO {
    @ApiModelProperty(value = "总里程(公里)")
    private BigDecimal totalMileage;
    @ApiModelProperty(value = "总时长(分钟)")
    private BigDecimal totalDuration;
}
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/OrderStatisticsDTO.java
New file
@@ -0,0 +1,33 @@
package com.xiaopeng.api.dto.statistics;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
 * 订单统计数据DTO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "订单统计数据DTO")
public class OrderStatisticsDTO {
    @ApiModelProperty(value = "订单总数(状态9=待评价或10=已完成)")
    private Long totalCount;
    @ApiModelProperty(value = "时间范围内新增订单数")
    private Long newCount;
    @ApiModelProperty(value = "总交易金额(pay_money累加)")
    private BigDecimal totalMoney;
    @ApiModelProperty(value = "总收入(pay_money累加)")
    private BigDecimal totalIncome;
    @ApiModelProperty(value = "每日新增订单数列表")
    private List<DailyCountDTO> dailyCountList;
}
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/StatisticsQueryDTO.java
New file
@@ -0,0 +1,26 @@
package com.xiaopeng.api.dto.statistics;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDate;
/**
 * 统计查询参数DTO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "统计查询参数DTO")
public class StatisticsQueryDTO {
    @ApiModelProperty(value = "开始日期")
    private LocalDate startDate;
    @ApiModelProperty(value = "结束日期")
    private LocalDate endDate;
    @ApiModelProperty(value = "趋势天数(默认30)")
    private Integer trendDays = 30;
}
xp-api/src/main/java/com/xiaopeng/api/dto/statistics/UserStatisticsDTO.java
New file
@@ -0,0 +1,26 @@
package com.xiaopeng.api.dto.statistics;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * 用户统计数据DTO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "用户统计数据DTO")
public class UserStatisticsDTO {
    @ApiModelProperty(value = "用户总数")
    private Long totalCount;
    @ApiModelProperty(value = "时间范围内新增用户数")
    private Long newCount;
    @ApiModelProperty(value = "每日新增用户数列表")
    private List<DailyCountDTO> dailyCountList;
}
xp-api/src/main/java/com/xiaopeng/api/factory/account/XpAppUserClientFallbackFactory.java
New file
@@ -0,0 +1,38 @@
package com.xiaopeng.api.factory.account;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.dto.statistics.UserStatisticsDTO;
import com.xiaopeng.api.feignClient.account.XpAppUserClient;
import com.xiaopeng.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
/**
 * 小程序用户Feign客户端降级工厂
 *
 * @author mitao
 */
@Slf4j
@Component
public class XpAppUserClientFallbackFactory implements FallbackFactory<XpAppUserClient> {
    @Override
    public XpAppUserClient create(Throwable cause) {
        log.error("用户服务调用失败: {}", cause.getMessage());
        return new XpAppUserClient() {
            @Override
            public R<UserStatisticsDTO> getUserStatistics(StatisticsQueryDTO query) {
                log.error("获取用户统计数据失败,原因: {}", cause.getMessage());
                // 降级返回空数据
                UserStatisticsDTO dto = new UserStatisticsDTO();
                dto.setTotalCount(0L);
                dto.setNewCount(0L);
                dto.setDailyCountList(new ArrayList<>());
                return R.ok(dto);
            }
        };
    }
}
xp-api/src/main/java/com/xiaopeng/api/factory/operation/RemoteMenuFallbackFactory.java
File was renamed from xp-api/src/main/java/com/xiaopeng/api/factory/RemoteMenuFallbackFactory.java
@@ -1,6 +1,6 @@
package com.xiaopeng.api.factory;
package com.xiaopeng.api.factory.operation;
import com.xiaopeng.api.feign.operation.RemoteMenuService;
import com.xiaopeng.api.feignClient.operation.SysMenuClient;
import com.xiaopeng.api.model.operation.SysMenu;
import com.xiaopeng.common.core.domain.R;
import org.slf4j.Logger;
@@ -12,14 +12,14 @@
import java.util.List;
@Component
public class RemoteMenuFallbackFactory implements FallbackFactory<RemoteMenuService> {
public class RemoteMenuFallbackFactory implements FallbackFactory<SysMenuClient> {
    private static final Logger log = LoggerFactory.getLogger(RemoteMenuFallbackFactory.class);
    @Override
    public RemoteMenuService create(Throwable cause) {
    public SysMenuClient create(Throwable cause) {
        log.error("菜单服务调用失败: {}", cause.getMessage());
        return new RemoteMenuService(){
        return new SysMenuClient(){
            @Override
            public R<List<SysMenu>> getMenusByMenuIds(List<Long> menuIds) {
xp-api/src/main/java/com/xiaopeng/api/factory/order/XpOrderClientFallbackFactory.java
New file
@@ -0,0 +1,41 @@
package com.xiaopeng.api.factory.order;
import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.ArrayList;
/**
 * 订单Feign客户端降级工厂
 *
 * @author mitao
 */
@Slf4j
@Component
public class XpOrderClientFallbackFactory implements FallbackFactory<XpOrderClient> {
    @Override
    public XpOrderClient create(Throwable cause) {
        log.error("订单服务调用失败: {}", cause.getMessage());
        return new XpOrderClient() {
            @Override
            public R<OrderStatisticsDTO> getOrderStatistics(StatisticsQueryDTO query) {
                log.error("获取订单统计数据失败,原因: {}", cause.getMessage());
                // 降级返回空数据
                OrderStatisticsDTO dto = new OrderStatisticsDTO();
                dto.setTotalCount(0L);
                dto.setNewCount(0L);
                dto.setTotalMoney(BigDecimal.ZERO);
                dto.setTotalIncome(BigDecimal.ZERO);
                dto.setDailyCountList(new ArrayList<>());
                return R.ok(dto);
            }
        };
    }
}
xp-api/src/main/java/com/xiaopeng/api/feign/operation/RemoteMenuService.java
File was deleted
xp-api/src/main/java/com/xiaopeng/api/feignClient/account/XpAppUserClient.java
New file
@@ -0,0 +1,30 @@
package com.xiaopeng.api.feignClient.account;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.dto.statistics.UserStatisticsDTO;
import com.xiaopeng.api.factory.account.XpAppUserClientFallbackFactory;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
 * 小程序用户Feign客户端
 *
 * @author mitao
 */
@FeignClient(contextId = "XpAppUserClient",
        value = ServiceNameConstantsEnum.XP_ACCOUNT_SERVICE,
        fallbackFactory = XpAppUserClientFallbackFactory.class)
public interface XpAppUserClient {
    /**
     * 获取用户统计数据
     *
     * @param query 查询参数
     * @return 用户统计数据
     */
    @PostMapping("/inner/appUser/statistics")
    R<UserStatisticsDTO> getUserStatistics(@RequestBody StatisticsQueryDTO query);
}
xp-api/src/main/java/com/xiaopeng/api/feignClient/operation/SysMenuClient.java
New file
@@ -0,0 +1,31 @@
package com.xiaopeng.api.feignClient.operation;
import com.xiaopeng.api.factory.operation.RemoteMenuFallbackFactory;
import com.xiaopeng.api.model.operation.SysMenu;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
 * 菜单服务
 *
 * @author ruoyi
 */
@FeignClient(contextId = "SysMenuClient", value = ServiceNameConstantsEnum.XP_OPERATION_SERVICE, fallbackFactory = RemoteMenuFallbackFactory.class)
public interface SysMenuClient
{
    /**
     * 根据菜单ID列表获取菜单列表
     *
     * @param menuIds 菜单ID列表
     * @return 菜单列表
     */
    @PostMapping("/menu/getMenusByMenuIds")
    R<List<SysMenu>> getMenusByMenuIds(@RequestBody List<Long> menuIds);
}
xp-api/src/main/java/com/xiaopeng/api/feignClient/order/XpOrderClient.java
New file
@@ -0,0 +1,30 @@
package com.xiaopeng.api.feignClient.order;
import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.factory.order.XpOrderClientFallbackFactory;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
 * 订单Feign客户端
 *
 * @author mitao
 */
@FeignClient(contextId = "XpOrderClient",
        value = ServiceNameConstantsEnum.XP_ORDER_SERVICE,
        fallbackFactory = XpOrderClientFallbackFactory.class)
public interface XpOrderClient {
    /**
     * 获取订单统计数据
     *
     * @param query 查询参数
     * @return 订单统计数据
     */
    @PostMapping("/inner/order/statistics")
    R<OrderStatisticsDTO> getOrderStatistics(@RequestBody StatisticsQueryDTO query);
}
xp-api/src/main/java/com/xiaopeng/api/model/account/SysRole.java
@@ -1,11 +1,8 @@
package com.xiaopeng.api.model.account;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xiaopeng.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -43,4 +40,14 @@
    @ApiModelProperty(value = "备注")
    private String remarks;
    public boolean isAdmin()
    {
        return isAdmin(this.id);
    }
    public static boolean isAdmin(Long roleId)
    {
        return roleId != null && 1L == roleId;
    }
}
xp-api/src/main/java/com/xiaopeng/api/model/account/SysRoleBak.java
@@ -25,7 +25,7 @@
    /** 角色ID */
    @Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
    @TableField("id")
    private Long id;
    private Long roleId;
    /** 角色名称 */
    @Excel(name = "角色名称")
@@ -81,22 +81,20 @@
    public SysRoleBak(Long id)
    {
        this.id = id;
        this.roleId = id;
    }
    public Long getId()
    {
        return id;
    public Long getRoleId() {
        return roleId;
    }
    public void setId(Long id)
    {
        this.id = id;
    public void setRoleId(Long roleId) {
        this.roleId = roleId;
    }
    public boolean isAdmin()
    {
        return isAdmin(this.id);
        return isAdmin(this.roleId);
    }
    public static boolean isAdmin(Long roleId)
@@ -217,7 +215,7 @@
    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
            .append("roleId", getId())
            .append("roleId", getRoleId())
            .append("roleName", getRoleName())
            .append("roleKey", getRoleKey())
            .append("dataScope", getDataScope())
xp-api/src/main/java/com/xiaopeng/api/model/account/SysUser.java
@@ -35,18 +35,20 @@
    private String userAccount;
    /** 用户昵称 */
    @Excel(name = "用户名称")
    @Excel(name = "用户昵称")
    @NotBlank(message = "姓名不能为空")
    @TableField("nick_name")
    @ApiModelProperty(value = "用户昵称")
    private String nickName;
    /** 手机号码 */
    @Excel(name = "手机号码")
    @NotBlank(message = "请输入账号")
    @NotBlank(message = "请输入手机号码")
    @ApiModelProperty(value = "手机号码")
    private String phoneNumber;
    /** 用户头像 */
    @TableField(exist = false)
    @ApiModelProperty(value = "用户头像")
    private String avatar;
    /** 密码 */
@@ -54,6 +56,7 @@
    /** 帐号状态(0正常 1停用) */
    @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
    @ApiModelProperty(value = "帐号状态 0=正常,1=停用")
    private String status;
    @ApiModelProperty(value = "备注")
@@ -61,7 +64,7 @@
    /** 角色对象 */
    @TableField(exist = false)
    private List<SysRole> roles;
    private List<SysRoleBak> roles;
    /** 角色组 */
    @TableField(exist = false)
@@ -71,8 +74,26 @@
    @TableField(exist = false)
    private Long[] postIds;
    /** 角色ID */
    @TableField(exist = false)
    @ApiModelProperty(value = "角色ID")
    private Long roleId;
    @ApiModelProperty(value = "角色")
    @TableField(exist = false)
    private List<String> roleNames;
    @TableField(exist = false)
    private Long deptId;
    public boolean isAdmin()
    {
        return isAdmin(this.id);
    }
    public static boolean isAdmin(Long userId)
    {
        return userId != null && 1L == userId;
    }
}
xp-api/src/main/java/com/xiaopeng/api/model/operation/TreeSelect.java
@@ -1,9 +1,10 @@
package com.xiaopeng.api.model.operation;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
 * Treeselect树结构实体类
xp-api/src/main/java/com/xiaopeng/api/model/operation/XpWhitelist.java
@@ -47,6 +47,6 @@
    private Boolean isPriorityDispatch;
    @ApiModelProperty(value = "来源 1=添加 2=邀请码")
    private Integer source;
    private Integer whitelistSource;
}
xp-api/src/main/java/com/xiaopeng/api/vo/account/LoginUser.java
@@ -2,10 +2,9 @@
import com.xiaopeng.api.model.account.SysUser;
import lombok.Data;
import java.awt.*;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
/**
@@ -13,6 +12,7 @@
 *
 * @author ruoyi
 */
@Data
public class LoginUser implements Serializable
{
    private static final long serialVersionUID = 1L;
@@ -50,7 +50,7 @@
    /**
     * 权限列表
     */
    private List<Menu> permissions;
    private Set<String> permissions;
    /**
     * 角色列表
@@ -66,99 +66,4 @@
     */
    private String userType;
    public String getToken()
    {
        return token;
    }
    public void setToken(String token)
    {
        this.token = token;
    }
    public Long getUserid()
    {
        return userid;
    }
    public void setUserid(Long userid)
    {
        this.userid = userid;
    }
    public String getUsername()
    {
        return username;
    }
    public void setUsername(String username)
    {
        this.username = username;
    }
    public Long getLoginTime()
    {
        return loginTime;
    }
    public void setLoginTime(Long loginTime)
    {
        this.loginTime = loginTime;
    }
    public Long getExpireTime()
    {
        return expireTime;
    }
    public void setExpireTime(Long expireTime)
    {
        this.expireTime = expireTime;
    }
    public String getIpaddr()
    {
        return ipaddr;
    }
    public void setIpaddr(String ipaddr)
    {
        this.ipaddr = ipaddr;
    }
    public List<Menu> getPermissions() {
        return permissions;
    }
    public void setPermissions(List<Menu> permissions) {
        this.permissions = permissions;
    }
    public Set<String> getRoles()
    {
        return roles;
    }
    public void setRoles(Set<String> roles)
    {
        this.roles = roles;
    }
    public SysUser getSysUser()
    {
        return sysUser;
    }
    public void setSysUser(SysUser sysUser)
    {
        this.sysUser = sysUser;
    }
    public String getUserType() {
        return userType;
    }
    public void setUserType(String userType) {
        this.userType = userType;
    }
}
xp-common/xp-common-core/src/main/java/com/xiaopeng/common/core/web/page/BasePage.java
@@ -2,6 +2,7 @@
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * <p></p>
@@ -9,43 +10,17 @@
 * @author mouseyCat
 * @date 2020/8/24 16:39
 */
@Data
@ApiModel(value = "基础查询列表dto")
public class BasePage {
    /**
     * 分页参数,当前页码
     */
    @ApiModelProperty(value = "分页参数,当前页码")
    private Integer pageCurr = 1;
    private Integer pageNum = 1;
    /**
     * 分页参数,每页数量
     */
    @ApiModelProperty(value = "分页参数,每页数量,默认为10")
    private Integer pageSize = 50;
    public Integer getPageCurr() {
//        if(null != pageCurr){
//            pageCurr = (pageCurr - 1) * pageSize;
//        }
        return pageCurr;
    }
    public void setPageCurr(Integer pageCurr) {
        this.pageCurr = pageCurr;
    }
    public Integer getPageSize() {
        return pageSize;
    }
    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }
    @Override
    public String toString() {
        return "BasePage{" +
                "pageCurr=" + pageCurr +
                ", pageSize=" + pageSize +
                '}';
    }
}
xp-common/xp-common-datascope/src/main/java/com/xiaopeng/common/datascope/aspect/DataScopeAspect.java
@@ -1,21 +1,20 @@
package com.xiaopeng.common.datascope.aspect;
import java.util.ArrayList;
import java.util.List;
import com.xiaopeng.api.model.account.SysRole;
import com.xiaopeng.api.model.account.SysRoleBak;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.vo.account.LoginUser;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.xiaopeng.common.core.context.SecurityContextHolder;
import com.xiaopeng.common.core.text.Convert;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.domain.BaseEntity;
import com.xiaopeng.common.datascope.annotation.DataScope;
import com.xiaopeng.common.security.utils.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.ArrayList;
import java.util.List;
/**
 * 数据过滤处理
@@ -23,7 +22,7 @@
 * @author ruoyi
 */
@Aspect
@Component
// @Component
public class DataScopeAspect
{
    /**
@@ -94,7 +93,7 @@
        StringBuilder sqlString = new StringBuilder();
        List<String> conditions = new ArrayList<String>();
        for (SysRole role : user.getRoles())
        for (SysRoleBak role : user.getRoles())
        {
            String dataScope = role.getDataScope();
            if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
@@ -132,7 +131,7 @@
            {
                if (StringUtils.isNotBlank(userAlias))
                {
                    sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
                    sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getId()));
                }
                else
                {
xp-service/pom.xml
@@ -30,7 +30,22 @@
    </description>
    <dependencies>
        <!-- easypoi -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>4.4.0</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
@@ -40,7 +55,7 @@
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.0.3</version>
            <version>5.8.38</version>
        </dependency>
        <dependency>
            <groupId>com.xiaopeng</groupId>
xp-service/xp-account/src/main/java/com/xiaopeng/account/controller/SysRoleBakController.java
New file
@@ -0,0 +1,256 @@
package com.xiaopeng.account.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xiaopeng.account.mapper.SysRoleMenuMapper;
import com.xiaopeng.account.service.ISysDeptService;
import com.xiaopeng.account.service.ISysRoleBakService;
import com.xiaopeng.account.service.ISysUserRoleService;
import com.xiaopeng.account.service.ISysUserService;
import com.xiaopeng.account.vo.RoleAddDto;
import com.xiaopeng.account.vo.RoleInfoVo;
import com.xiaopeng.account.vo.RoleUpdateDto;
import com.xiaopeng.api.model.account.SysRoleBak;
import com.xiaopeng.api.model.account.SysRoleMenu;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.account.SysUserRole;
import com.xiaopeng.api.model.operation.SysDept;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.controller.BaseController;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.common.core.web.page.BasePage;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.core.web.page.TableDataInfo;
import com.xiaopeng.common.log.annotation.Log;
import com.xiaopeng.common.log.enums.BusinessType;
import com.xiaopeng.common.security.annotation.Logical;
import com.xiaopeng.common.security.annotation.RequiresPermissions;
import com.xiaopeng.common.security.utils.SecurityUtils;
import io.seata.spring.annotation.GlobalTransactional;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
 * 角色信息
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/role")
@Api(tags = "角色模块")
public class SysRoleBakController extends BaseController {
    @Resource
    private ISysRoleBakService roleService;
    @Resource
    private ISysUserService userService;
    @Resource
    private ISysDeptService deptService;
    @Resource
    private SysRoleMenuMapper sysRoleMenuMapper;
    @Resource
    private ISysUserRoleService sysUserRoleService;
    @RequiresPermissions(value = {"/systemUserManagement", "/systemUserManagement/add", "/systemUserManagement/update", "/roleManagement", "/partners",
            "/partners/add", "/partners/seleete", "/partners/update"}, logical = Logical.OR)
    @ApiOperation(value = "获取角色列表", tags = {"管理后台-系统用户管理"})
    @GetMapping("/list")
    public AjaxResult list() {
        List<SysRoleBak> list = roleService.list(new LambdaQueryWrapper<SysRoleBak>().eq(SysRoleBak::getDelFlag, 0).eq(SysRoleBak::getStatus, 0));
        return AjaxResult.success(list);
    }
    @RequiresPermissions(value = {"/roleManagement"}, logical = Logical.OR)
    @ResponseBody
    @GetMapping("/listPage")
    @ApiOperation(value = "获取角色列表", tags = {"管理后台-角色管理"})
    public AjaxResult listPage(String name, BasePage basePage) {
        PageInfo<SysRoleBak> pageInfo = new PageInfo<>(basePage.getPageNum(), basePage.getPageSize());
        LambdaQueryWrapper<SysRoleBak> wrapper = new LambdaQueryWrapper<SysRoleBak>().eq(SysRoleBak::getStatus, 0).eq(SysRoleBak::getDelFlag, 0);
        if (StringUtils.isNotEmpty(name)) {
            wrapper.like(SysRoleBak::getRoleName, name);
        }
        PageInfo<SysRoleBak> page = roleService.page(pageInfo, wrapper.orderByDesc(SysRoleBak::getCreateTime));
        for (SysRoleBak record : page.getRecords()) {
            long count = sysUserRoleService.count(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getRoleId, record.getId()));
            record.setNumber(count);
        }
        return AjaxResult.success(page);
    }
    @RequiresPermissions(value = {"/roleManagement/add"}, logical = Logical.OR)
    @ResponseBody
    @PostMapping("/roleAdd")
    @ApiOperation(value = "添加角色", tags = {"管理后台-角色管理"})
    @GlobalTransactional(rollbackFor = Exception.class)//分布式事务
    public AjaxResult roleAdd(@Validated @RequestBody RoleAddDto dto) {
        SysRoleBak role = new SysRoleBak();
        role.setRoleName(dto.getRoleName());
        long count = roleService.count(Wrappers.lambdaQuery(SysRoleBak.class)
                .eq(SysRoleBak::getRoleName, dto.getRoleName()));
        if (count > 0) {
            return AjaxResult.error("角色已存在,请重新输入");
        }
        List<Long> menuIds1 = dto.getMenuIds();
        if (CollectionUtils.isEmpty(menuIds1)) {
            return AjaxResult.error("菜单id不能为空");
        }
        role.setMenuIds(dto.getMenuIds().toArray((new Long[dto.getMenuIds().size()])));
        // 添加角色
        role.setRemark(dto.getRemark());
        role.setCreateBy(SecurityUtils.getUsername());
        role.setCreateTime(new Date());
        roleService.insertRole(role);
        return AjaxResult.success();
    }
    @RequiresPermissions(value = {"/roleManagement/info", "/roleManagement/update"}, logical = Logical.OR)
    @GetMapping("/roleInfo")
    @ApiOperation(value = "角色详情", tags = {"管理后台-角色管理"})
    public AjaxResult roleInfo(@RequestParam Long id) {
        SysRoleBak role = roleService.selectRoleById(id);
        RoleInfoVo roleInfoVo = new RoleInfoVo();
        roleInfoVo.setRoleId(role.getId());
        roleInfoVo.setRoleName(role.getRoleName());
        // 获取当前角色的菜单id
        List<Long> menusId = sysRoleMenuMapper.selectList(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, id)).stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());
        if (menusId.size() == 0) {
            return AjaxResult.success(new ArrayList<>());
        }
        //获取当前的权限菜单
        // List<SysMenus> all = menuMapper.getAllInIds(menusId);
        // 第三级
        // List<SysMenus> s3 = all.stream().filter(e -> e.getMenuType().equals("F")).collect(Collectors.toList());
        // // 第二级
        // List<SysMenus> s2 = all.stream().filter(e -> e.getMenuType().equals("C")).collect(Collectors.toList());
        // // 第一级
        // List<SysMenus> s1 = all.stream().filter(e -> e.getMenuType().equals("M")).collect(Collectors.toList());
        // for (SysMenus menus : s2) {
        //     List<SysMenus> collect = s3.stream().filter(e -> e.getParentId().equals(menus.getMenuId())).collect(Collectors.toList());
        //     menus.setChildren(collect);
        // }
        //
        // for (SysMenus menus : s1) {
        //     List<SysMenus> collect = s2.stream().filter(e -> e.getParentId().equals(menus.getMenuId())).collect(Collectors.toList());
        //     menus.setChildren(collect);
        // }
        roleInfoVo.setMenus(menusId);
        roleInfoVo.setRemark(role.getRemark());
        return AjaxResult.success(roleInfoVo);
    }
    @RequiresPermissions(value = {"/roleManagement/update"}, logical = Logical.OR)
    @PostMapping("/roleUpdate")
    @ApiOperation(value = "编辑角色", tags = {"管理后台-角色管理"})
    @GlobalTransactional(rollbackFor = Exception.class)//分布式事务
    public AjaxResult roleUpdate(@Validated @RequestBody RoleUpdateDto dto) {
        SysRoleBak role = new SysRoleBak();
        role.setRoleName(dto.getRoleName());
        SysRoleBak one = roleService.getOne(new LambdaQueryWrapper<SysRoleBak>().eq(SysRoleBak::getRoleName, dto.getRoleName()));
        if (null != one && !one.getId().equals(dto.getRoleId())) {
            return AjaxResult.error("角色已存在,请重新输入");
        }
        role.setRemark(dto.getRemark());
        role.setUpdateBy(SecurityUtils.getUsername());
        role.setUpdateTime(new Date());
        role.setId(dto.getRoleId());
        roleService.updateRole(role);
        ArrayList<SysRoleMenu> sysRoleMenus = new ArrayList<>();
        List<Long> menuIds = dto.getMenuIds();
        // 移除原来的权限菜单
        if (menuIds.contains(1061L)) {
            sysRoleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>()
                    .eq(SysRoleMenu::getRoleId, dto.getRoleId()));
        } else {
            sysRoleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>()
                    .eq(SysRoleMenu::getRoleId, dto.getRoleId())
                    .ne(SysRoleMenu::getMenuId, 1061L)
                    .ne(SysRoleMenu::getMenuId, 1062L)
                    .ne(SysRoleMenu::getMenuId, 1065L)
                    .ne(SysRoleMenu::getMenuId, 1073L)
                    .ne(SysRoleMenu::getMenuId, 1161L)
                    .ne(SysRoleMenu::getMenuId, 1203L)
            );
        }
        for (Long menuId : menuIds) {
            SysRoleMenu sysRoleMenu = new SysRoleMenu();
            sysRoleMenu.setMenuId(menuId);
            sysRoleMenu.setRoleId(role.getId());
            sysRoleMenus.add(sysRoleMenu);
        }
        sysRoleMenuMapper.batchRoleMenu(sysRoleMenus);
        return AjaxResult.success();
    }
    /**
     * 删除角色
     */
    @RequiresPermissions(value = {"/roleManagement/del"}, logical = Logical.OR)
    @Log(title = "角色管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{roleIds}")
    @ApiOperation(value = "删除角色", tags = {"管理后台-角色管理"})
    public AjaxResult remove(@PathVariable Long[] roleIds) {
        return toAjax(roleService.deleteRoleByIds(roleIds));
    }
    /**
     * 查询已分配用户角色列表
     */
    @RequiresPermissions("/roleManagement")
    @GetMapping("/authUser/allocatedList")
    public TableDataInfo allocatedList(SysUser user) {
        startPage();
        List<SysUser> list = userService.selectAllocatedList(user);
        return getDataTable(list);
    }
    /**
     * 查询未分配用户角色列表
     */
    @RequiresPermissions("/roleManagement")
    @GetMapping("/authUser/unallocatedList")
    public TableDataInfo unallocatedList(SysUser user) {
        startPage();
        List<SysUser> list = userService.selectUnallocatedList(user);
        return getDataTable(list);
    }
    /**
     * 获取对应角色部门树列表
     */
    @RequiresPermissions("")
    @GetMapping(value = "/deptTree/{roleId}")
    public AjaxResult deptTree(@PathVariable("roleId") Long roleId) {
        AjaxResult ajax = AjaxResult.success();
        ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
        ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
        return ajax;
    }
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/controller/SysRoleController.java
@@ -1,252 +1,68 @@
package com.xiaopeng.account.controller;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.page.BasePage;
import com.xiaopeng.account.model.dto.SysRoleAddDTO;
import com.xiaopeng.account.model.dto.SysRoleEditDTO;
import com.xiaopeng.account.model.query.SysRolePageQuery;
import com.xiaopeng.account.model.vo.SysRoleDetailVO;
import com.xiaopeng.account.model.vo.SysRolePageVO;
import com.xiaopeng.account.service.ISysRoleService;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.security.annotation.Logical;
import com.xiaopeng.account.mapper.SysRoleMenuMapper;
import com.xiaopeng.account.service.ISysDeptService;
import com.xiaopeng.account.service.ISysRoleBakService;
import com.xiaopeng.account.service.ISysUserRoleService;
import com.xiaopeng.account.service.ISysUserService;
import com.xiaopeng.account.vo.RoleAddDto;
import com.xiaopeng.account.vo.RoleInfoVo;
import com.xiaopeng.account.vo.RoleUpdateDto;import com.xiaopeng.api.model.account.*;
import com.xiaopeng.api.model.operation.SysDept;
import com.xiaopeng.api.model.operation.SysMenus;import io.seata.spring.annotation.GlobalTransactional;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.CollectionUtils;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.xiaopeng.common.core.web.controller.BaseController;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.common.core.web.page.TableDataInfo;
import com.xiaopeng.common.log.annotation.Log;
import com.xiaopeng.common.log.enums.BusinessType;
import com.xiaopeng.common.security.annotation.RequiresPermissions;
import com.xiaopeng.common.security.utils.SecurityUtils;
/**
 * 角色信息
 * <p>
 * 角色管理 前端控制器
 * </p>
 *
 * @author ruoyi
 * @author mitao
 * @since 2026-02-03
 */
@Api(tags = "角色管理")
@RequiredArgsConstructor(onConstructor_ = @Lazy)
@RestController
@RequestMapping("/role")
@Api(tags = "角色模块")
public class SysRoleController extends BaseController {
    @Resource
    private ISysRoleBakService roleService;
    @Resource
    private ISysUserService userService;
    @Resource
    private ISysDeptService deptService;
    @Resource
    private SysRoleMenuMapper sysRoleMenuMapper;
    @Resource
    private ISysUserRoleService sysUserRoleService;
public class SysRoleController {
    private final ISysRoleService sysRoleService;
    @ApiOperation("新增角色")
    @PostMapping("/api/v1/role/add")
    public R<?> add(@Validated @RequestBody SysRoleAddDTO dto) {
        sysRoleService.addRole(dto);
        return R.ok();
    }
    @RequiresPermissions(value = {"/systemUserManagement", "/systemUserManagement/add", "/systemUserManagement/update", "/roleManagement", "/partners",
            "/partners/add", "/partners/seleete", "/partners/update"}, logical = Logical.OR)
    @ApiOperation(value = "获取角色列表", tags = {"管理后台-系统用户管理"})
    @GetMapping("/list")
    public AjaxResult list() {
        List<SysRoleBak> list = roleService.list(new LambdaQueryWrapper<SysRoleBak>().eq(SysRoleBak::getDelFlag, 0).eq(SysRoleBak::getStatus, 0));
        return AjaxResult.success(list);
    }
    @RequiresPermissions(value = {"/roleManagement"}, logical = Logical.OR)
    @ResponseBody
    @GetMapping("/listPage")
    @ApiOperation(value = "获取角色列表", tags = {"管理后台-角色管理"})
    public AjaxResult listPage(String name, BasePage basePage) {
        PageInfo<SysRoleBak> pageInfo = new PageInfo<>(basePage.getPageCurr(), basePage.getPageSize());
        LambdaQueryWrapper<SysRoleBak> wrapper = new LambdaQueryWrapper<SysRoleBak>().eq(SysRoleBak::getStatus, 0).eq(SysRoleBak::getDelFlag, 0);
        if (StringUtils.isNotEmpty(name)) {
            wrapper.like(SysRoleBak::getRoleName, name);
        }
        PageInfo<SysRoleBak> page = roleService.page(pageInfo, wrapper.orderByDesc(SysRoleBak::getCreateTime));
        for (SysRoleBak record : page.getRecords()) {
            long count = sysUserRoleService.count(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getRoleId, record.getId()));
            record.setNumber(count);
        }
        return AjaxResult.success(page);
    }
    @RequiresPermissions(value = {"/roleManagement/add"}, logical = Logical.OR)
    @ResponseBody
    @PostMapping("/roleAdd")
    @ApiOperation(value = "添加角色", tags = {"管理后台-角色管理"})
    @GlobalTransactional(rollbackFor = Exception.class)//分布式事务
    public AjaxResult roleAdd(@Validated @RequestBody RoleAddDto dto) {
        SysRoleBak role = new SysRoleBak();
        role.setRoleName(dto.getRoleName());
        long count = roleService.count(Wrappers.lambdaQuery(SysRoleBak.class)
                .eq(SysRoleBak::getRoleName, dto.getRoleName()));
        if (count > 0) {
            return AjaxResult.error("角色已存在,请重新输入");
        }
        List<Long> menuIds1 = dto.getMenuIds();
        if (CollectionUtils.isEmpty(menuIds1)) {
            return AjaxResult.error("菜单id不能为空");
        }
        role.setMenuIds(dto.getMenuIds().toArray((new Long[dto.getMenuIds().size()])));
        // 添加角色
        role.setRemark(dto.getRemark());
        role.setCreateBy(SecurityUtils.getUsername());
        role.setCreateTime(new Date());
        roleService.insertRole(role);
        return AjaxResult.success();
    }
    @RequiresPermissions(value = {"/roleManagement/info", "/roleManagement/update"}, logical = Logical.OR)
    @GetMapping("/roleInfo")
    @ApiOperation(value = "角色详情", tags = {"管理后台-角色管理"})
    public AjaxResult roleInfo(@RequestParam Long id) {
        SysRoleBak role = roleService.selectRoleById(id);
        RoleInfoVo roleInfoVo = new RoleInfoVo();
        roleInfoVo.setRoleId(role.getId());
        roleInfoVo.setRoleName(role.getRoleName());
        // 获取当前角色的菜单id
        List<Long> menusId = sysRoleMenuMapper.selectList(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, id)).stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());
        if (menusId.size() == 0) {
            return AjaxResult.success(new ArrayList<>());
        }
        //获取当前的权限菜单
        List<SysMenus> all = menuMapper.getAllInIds(menusId);
        // 第三级
        List<SysMenus> s3 = all.stream().filter(e -> e.getMenuType().equals("F")).collect(Collectors.toList());
        // 第二级
        List<SysMenus> s2 = all.stream().filter(e -> e.getMenuType().equals("C")).collect(Collectors.toList());
        // 第一级
        List<SysMenus> s1 = all.stream().filter(e -> e.getMenuType().equals("M")).collect(Collectors.toList());
        for (SysMenus menus : s2) {
            List<SysMenus> collect = s3.stream().filter(e -> e.getParentId().equals(menus.getMenuId())).collect(Collectors.toList());
            menus.setChildren(collect);
        }
        for (SysMenus menus : s1) {
            List<SysMenus> collect = s2.stream().filter(e -> e.getParentId().equals(menus.getMenuId())).collect(Collectors.toList());
            menus.setChildren(collect);
        }
        roleInfoVo.setMenus(menusId);
        roleInfoVo.setRemark(role.getRemark());
        return AjaxResult.success(roleInfoVo);
    }
    @RequiresPermissions(value = {"/roleManagement/update"}, logical = Logical.OR)
    @PostMapping("/roleUpdate")
    @ApiOperation(value = "编辑角色", tags = {"管理后台-角色管理"})
    @GlobalTransactional(rollbackFor = Exception.class)//分布式事务
    public AjaxResult roleUpdate(@Validated @RequestBody RoleUpdateDto dto) {
        SysRoleBak role = new SysRoleBak();
        role.setRoleName(dto.getRoleName());
        SysRoleBak one = roleService.getOne(new LambdaQueryWrapper<SysRoleBak>().eq(SysRoleBak::getRoleName, dto.getRoleName()));
        if (null != one && !one.getId().equals(dto.getRoleId())) {
            return AjaxResult.error("角色已存在,请重新输入");
        }
        role.setRemark(dto.getRemark());
        role.setUpdateBy(SecurityUtils.getUsername());
        role.setUpdateTime(new Date());
        role.setId(dto.getRoleId());
        roleService.updateRole(role);
        ArrayList<SysRoleMenu> sysRoleMenus = new ArrayList<>();
        List<Long> menuIds = dto.getMenuIds();
        // 移除原来的权限菜单
        if (menuIds.contains(1061L)) {
            sysRoleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>()
                    .eq(SysRoleMenu::getRoleId, dto.getRoleId()));
        } else {
            sysRoleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>()
                    .eq(SysRoleMenu::getRoleId, dto.getRoleId())
                    .ne(SysRoleMenu::getMenuId, 1061L)
                    .ne(SysRoleMenu::getMenuId, 1062L)
                    .ne(SysRoleMenu::getMenuId, 1065L)
                    .ne(SysRoleMenu::getMenuId, 1073L)
                    .ne(SysRoleMenu::getMenuId, 1161L)
                    .ne(SysRoleMenu::getMenuId, 1203L)
            );
        }
        for (Long menuId : menuIds) {
            SysRoleMenu sysRoleMenu = new SysRoleMenu();
            sysRoleMenu.setMenuId(menuId);
            sysRoleMenu.setRoleId(role.getId());
            sysRoleMenus.add(sysRoleMenu);
        }
        sysRoleMenuMapper.batchRoleMenu(sysRoleMenus);
        return AjaxResult.success();
    }
    /**
     * 删除角色
     */
    @RequiresPermissions(value = {"/roleManagement/del"}, logical = Logical.OR)
    @Log(title = "角色管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{roleIds}")
    @ApiOperation(value = "删除角色", tags = {"管理后台-角色管理"})
    public AjaxResult remove(@PathVariable Long[] roleIds) {
        return toAjax(roleService.deleteRoleByIds(roleIds));
    }
    /**
     * 查询已分配用户角色列表
     */
    @RequiresPermissions("/roleManagement")
    @GetMapping("/authUser/allocatedList")
    public TableDataInfo allocatedList(SysUser user) {
        startPage();
        List<SysUser> list = userService.selectAllocatedList(user);
        return getDataTable(list);
    }
    /**
     * 查询未分配用户角色列表
     */
    @RequiresPermissions("/roleManagement")
    @GetMapping("/authUser/unallocatedList")
    public TableDataInfo unallocatedList(SysUser user) {
        startPage();
        List<SysUser> list = userService.selectUnallocatedList(user);
        return getDataTable(list);
    }
    /**
     * 获取对应角色部门树列表
     */
    @RequiresPermissions("")
    @GetMapping(value = "/deptTree/{roleId}")
    public AjaxResult deptTree(@PathVariable("roleId") Long roleId) {
        AjaxResult ajax = AjaxResult.success();
        ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
        ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
        return ajax;
    }
    @ApiOperation("编辑角色")
    @PostMapping("/api/v1/role/edit")
    public R<?> edit(@Validated @RequestBody SysRoleEditDTO dto) {
        sysRoleService.editRole(dto);
        return R.ok();
    }
    @ApiOperation("角色详情")
    @GetMapping("/api/v1/role/queryById")
    public R<SysRoleDetailVO> queryById(@ApiParam(value = "角色id", required = true) @RequestParam Long id) {
        SysRoleDetailVO detail = sysRoleService.getRoleDetail(id);
        return R.ok(detail);
    }
    @ApiOperation("角色分页列表")
    @PostMapping("/api/v1/role/pageList")
    public R<PageInfo<SysRolePageVO>> pageList(@Validated @RequestBody SysRolePageQuery query) {
        PageInfo<SysRolePageVO> pageInfo = sysRoleService.pageList(query);
        return R.ok(pageInfo);
    }
    @ApiOperation("删除角色")
    @DeleteMapping("/api/v1/role/delete/{id}")
    public R<?> delete(@ApiParam(value = "角色id", required = true) @PathVariable Long id) {
        sysRoleService.deleteRole(id);
        return R.ok();
    }
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/controller/SysUserController.java
@@ -1,36 +1,39 @@
package com.xiaopeng.account.controller;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.common.security.annotation.Logical;
import com.xiaopeng.account.model.dto.UserPwdResetDTO;
import com.xiaopeng.account.model.query.SysUserListQuery;
import com.xiaopeng.account.service.*;
import com.xiaopeng.account.vo.GetSysUserList;
import com.xiaopeng.account.vo.ShopUserStart;
import com.xiaopeng.api.model.account.SysUserRole;
import com.xiaopeng.api.model.operation.SysDept;
import com.xiaopeng.api.model.account.SysRole;
import com.xiaopeng.api.model.account.SysRoleBak;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.account.SysUserRole;
import com.xiaopeng.api.model.operation.SysDept;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.StringUtils;
import com.xiaopeng.common.core.web.controller.BaseController;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.log.annotation.Log;
import com.xiaopeng.common.log.enums.BusinessType;
import com.xiaopeng.common.security.annotation.Logical;
import com.xiaopeng.common.security.annotation.RequiresPermissions;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.common.security.utils.SecurityUtils;
import io.seata.spring.annotation.GlobalTransactional;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.web.controller.BaseController;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.common.log.annotation.Log;
import com.xiaopeng.common.log.enums.BusinessType;
import com.xiaopeng.common.security.annotation.RequiresPermissions;
import com.xiaopeng.common.security.utils.SecurityUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
 * 用户信息
@@ -38,15 +41,15 @@
 * @author ruoyi
 */
@RestController
@RequestMapping("/user")
@Api(tags = "用户信息")
public class SysUserController extends BaseController {
    @Resource
    private ISysUserService userService;
    @Resource
    private ISysRoleBakService roleService;
    private ISysRoleBakService roleBakService;
    @Resource
    private ISysRoleService sysRoleService;
    @Resource
    private ISysDeptService deptService;
@@ -64,11 +67,11 @@
     * 获取用户列表
     */
    @RequiresPermissions(value = {"/systemUserManagement", "/roleManagement/info"}, logical = Logical.OR)
    @GetMapping("/list")
    @ApiOperation(value = "获取系统用户列表", tags = {"管理后台-系统用户管理", "管理后台-角色管理"})
    public AjaxResult list(GetSysUserList getSysUserList) {
        PageInfo<SysUser> pageInfo = new PageInfo<>(getSysUserList.getPageCurr(), getSysUserList.getPageSize());
        PageInfo<SysUser> page = userService.getList(pageInfo, getSysUserList);
    @GetMapping("/api/v1/sys-user/pageList")
    @ApiOperation(value = "账号管理分页列表", tags = {"管理后台-账号管理"})
    public AjaxResult<PageInfo<SysUser>> list(SysUserListQuery sysUserListQuery) {
        PageInfo<SysUser> pageInfo = new PageInfo<>(sysUserListQuery.getPageNum(), sysUserListQuery.getPageSize());
        PageInfo<SysUser> page = userService.getList(pageInfo, sysUserListQuery);
        return AjaxResult.success(page);
    }
@@ -78,25 +81,25 @@
     * 新增用户
     */
    @RequiresPermissions(value = {"/systemUserManagement/add"}, logical = Logical.OR)
    @Log(title = "用户管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @ApiOperation(value = "添加系统用户", tags = {"管理后台-系统用户管理"})
    @Log(title = "帐号管理", businessType = BusinessType.INSERT)
    @PostMapping("/api/v1/sys-user/add")
    @ApiOperation(value = "添加帐号", tags = {"管理后台-帐号管理"})
    @GlobalTransactional(rollbackFor = Exception.class)//分布式事务
    public AjaxResult add(@RequestBody SysUser user) {
        user.setUserName(user.getPhonenumber());
        if(!org.springframework.util.StringUtils.hasLength(user.getNickName())){
            user.setNickName(user.getPhonenumber());
        }
        if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
        if (StringUtils.isNotEmpty(user.getPhoneNumber()) && !userService.checkPhoneUnique(user)) {
            return error("手机号已开通账号");
        }
        if (StringUtils.isNotEmpty(user.getUserName()) && !userService.checkUserNameUnique(user)) {
        if (StringUtils.isNotEmpty(user.getUserAccount()) && !userService.checkUserNameUnique(user)) {
            return error("登录账号重复");
        }
        //校验编号是否唯一
        Long count = userService.lambdaQuery().eq(SysUser::getUniqueNumber, user.getUniqueNumber()).count();
        if (count > 0) {
            return error("用户编号已存在");
        }
        user.setCreateBy(SecurityUtils.getUsername());
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        user.setRoleType(1);
        user.setPassWordUpdate(new Date());
        //TODO 手机号脱敏存储
        userService.insertUser(user);
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setRoleId(user.getRoleId());
@@ -111,7 +114,7 @@
    @ApiOperation(value = "校验账号是否重复", tags = {"管理后台-系统用户管理"})
    public AjaxResult verifyUserNameRepeat(@PathVariable String username){
        SysUser user = new SysUser();
        user.setUserName(username);
        user.setUserAccount(username);
        if (StringUtils.isNotEmpty(username) && !userService.checkUserNameUnique(user)) {
            return error("账号不可重复");
        }
@@ -127,18 +130,18 @@
     * 根据用户编号获取详细信息
     */
    @ApiOperation(value = "获取用户详情", tags = {"管理后台-系统用户管理"})
    @GetMapping("/getInfo/{userId}")
    @GetMapping("/api/v1/getInfo/{userId}")
    public AjaxResult getInfo(@PathVariable Long userId) {
        userService.checkUserDataScope(userId);
        AjaxResult ajax = AjaxResult.success();
        List<SysRoleBak> roles = roleService.selectRoleAll();
        List<SysRole> roles = sysRoleService.list();
        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
        ajax.put("posts", postService.selectPostAll());
        if (StringUtils.isNotNull(userId)) {
            SysUser sysUser = userService.selectUserById(userId);
            ajax.put(AjaxResult.DATA_TAG, sysUser);
            ajax.put("postIds", postService.selectPostListByUserId(userId));
            ajax.put("roleIds", sysUser.getRoles().stream().map(SysRoleBak::getId).collect(Collectors.toList()));
            // ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getId).collect(Collectors.toList()));
        }
        return AjaxResult.success(ajax);
    }
@@ -148,15 +151,11 @@
     * 修改用户
     */
    @RequiresPermissions(value = {"/systemUserManagement/update"}, logical = Logical.OR)
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/update")
    @ApiOperation(value = "编辑系统用户", tags = {"管理后台-系统用户管理"})
    @Log(title = "帐号管理", businessType = BusinessType.UPDATE)
    @PutMapping("/api/v1/sys-user/edit")
    @ApiOperation(value = "编辑帐号", tags = {"管理后台-帐号管理"})
    @GlobalTransactional(rollbackFor = Exception.class)//分布式事务
    public AjaxResult edit(@Validated @RequestBody SysUser user) {
        user.setUserName(user.getPhonenumber());
        if(!org.springframework.util.StringUtils.hasLength(user.getNickName())){
            user.setNickName(user.getPhonenumber());
        }
        R<Integer> admin = this.isAdmin(user.getId());
        Integer data = admin.getData();
        if(data == null || data != 1){
@@ -167,30 +166,23 @@
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getId());
        SysUser sysUser = userService.getOne(Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getPhonenumber, user.getPhonenumber())
                .eq(SysUser::getDelFlag,0)
                .eq(SysUser::getPhoneNumber, user.getPhoneNumber())
                .last("LIMIT 1"));
        if (StringUtils.isNotEmpty(user.getPhonenumber()) && (Objects.nonNull(sysUser) && !user.getId().equals(sysUser.getId()) )) {
        if (StringUtils.isNotEmpty(user.getPhoneNumber()) && (Objects.nonNull(sysUser) && !user.getId().equals(sysUser.getId()) )) {
            return error("手机号已开通账号");
        }
        sysUser = userService.getOne(Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getUserName, user.getUserName())
                .eq(SysUser::getDelFlag,0)
                .eq(SysUser::getUserAccount, user.getUserAccount())
                .last("LIMIT 1"));
        if (StringUtils.isNotEmpty(user.getUserName()) && (Objects.nonNull(sysUser) && !user.getId().equals(sysUser.getId()) )) {
        if (StringUtils.isNotEmpty(user.getUserAccount()) && (Objects.nonNull(sysUser) && !user.getId().equals(sysUser.getId()) )) {
            return error("登录账号重复");
        }
        user.setUpdateBy(SecurityUtils.getUsername());
        if (user.getPassword() != null && !"".equals(user.getPassword())) {
        if (user.getPassword() != null && !user.getPassword().isEmpty()) {
            user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
            user.setPassWordUpdate(new Date());
        }
        if (user.getPhonenumber() != null) {
            user.setUserName(user.getPhonenumber());
        }
        user.setUpdateBy(SecurityUtils.getUsername());
        user.setUpdateTime(new Date());
        userService.updateUser(user);
        
        //删除旧的角色数据,添加新的角色
@@ -209,9 +201,9 @@
     * 删除用户
     */
    @RequiresPermissions(value = {"/systemUserManagement/del"}, logical = Logical.OR)
    @Log(title = "用户管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{userIds}")
    @ApiOperation(value = "删除系统用户", tags = {"管理后台-系统用户管理"})
    @Log(title = "帐号管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/api/v1/sys-user/{userIds}")
    @ApiOperation(value = "删除账号", tags = {"管理后台-帐号管理"})
    public AjaxResult remove(@PathVariable Long[] userIds) {
        if (ArrayUtils.contains(userIds, tokenService.getLoginUser())) {
            return error("当前用户不能删除");
@@ -223,21 +215,16 @@
    @RequiresPermissions(value = {"/systemUserManagement/forbidden"}, logical = Logical.OR)
    @PostMapping("/shopUserStart")
    @ApiOperation(value = "账号管理--禁用/启用", tags = {"管理后台-系统用户管理"})
    public AjaxResult shopUserStart(@RequestBody ShopUserStart shopUserStart) {
        if (shopUserStart.getUserId() == null) {
            return AjaxResult.error("userId不能为空");
        }
        SysUser sysUser = userService.selectUserById(shopUserStart.getUserId());
    @PostMapping("/api/v1/sys-user/switchStatus/{id}")
    @ApiOperation(value = "账号管理--禁用/启用", tags = {"管理后台-帐号管理"})
    public AjaxResult shopUserStart(@ApiParam(name = "id", value = "帐号ID") @PathVariable("id") Long id) {
        SysUser sysUser = userService.selectUserById(id);
        if (sysUser.getStatus().equals("0")) {
            sysUser.setStatus("1");
            sysUser.setForbiddenRemark(shopUserStart.getRemark());
        } else {
            sysUser.setStatus("0");
            sysUser.setForbiddenRemark("");
        }
        return toAjax(userService.updateUser(sysUser));
        return toAjax(userService.updateById(sysUser));
    }
@@ -271,7 +258,6 @@
    public R<Boolean> updateSysUser(@RequestBody SysUser sysUser) {
        try {
            sysUser.setUpdateBy(SecurityUtils.getUsername());
            sysUser.setUpdateTime(new Date());
            userService.updateUser(sysUser);
            if(null != sysUser.getRoleId()){
                sysUserRoleService.deleteSysUserRoleByUserId(sysUser.getId());
@@ -298,10 +284,14 @@
     * 重置密码
     */
    @RequiresPermissions(value = {"/systemUserManagement/reset_passwords"}, logical = Logical.OR)
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/resetPwd")
    @ApiOperation(value = "重置密码", tags = {"管理后台-系统用户管理"})
    public AjaxResult resetPwd(@RequestBody SysUser user) {
    @Log(title = "帐号管理", businessType = BusinessType.UPDATE)
    @PutMapping("/api/v1/sys-user/rePassword")
    @ApiOperation(value = "重置密码", tags = {"管理后台-帐号管理"})
    public AjaxResult resetPwd(@RequestBody UserPwdResetDTO dto) {
        SysUser user = userService.getById(dto.getUserId());
        if (Objects.isNull(user)) {
            throw new ServiceException("用户不存在");
        }
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getId());
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
@@ -328,7 +318,7 @@
    public AjaxResult authRole(@PathVariable("userId") Long userId) {
        AjaxResult ajax = AjaxResult.success();
        SysUser user = userService.selectUserById(userId);
        List<SysRoleBak> roles = roleService.selectRolesByUserId(userId);
        List<SysRoleBak> roles = roleBakService.selectRolesByUserId(userId);
        ajax.put("user", user);
        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
        return ajax;
@@ -364,14 +354,14 @@
     * @return
     */
    @PostMapping("/queryRoleByUserId/{userId}")
    public R<SysRoleBak> queryRoleByUserId(@PathVariable("userId") Long userId){
    public R<SysRole> queryRoleByUserId(@PathVariable("userId") Long userId){
        SysUserRole one = sysUserRoleService.getOne(Wrappers.lambdaQuery(SysUserRole.class)
                .eq(SysUserRole::getUserId, userId)
                .last("LIMIT 1"));
        System.out.println("通过用户id查询角色对象"+one);
        if(Objects.nonNull(one)){
            SysRoleBak byId = roleService.getOne(Wrappers.lambdaQuery(SysRoleBak.class)
                    .eq(SysRoleBak::getId,one.getRoleId())
            SysRole byId = sysRoleService.getOne(Wrappers.lambdaQuery(SysRole.class)
                    .eq(SysRole::getId,one.getRoleId())
                    .last("LIMIT 1"));
            return R.ok(byId);
        }
@@ -380,9 +370,9 @@
    @PostMapping("/queryRoleByRoleId/{roleId}")
    public R<SysRoleBak> queryRoleByRoleId(@PathVariable("roleId") Long roleId){
        return R.ok( roleService.getOne(Wrappers.lambdaQuery(SysRoleBak.class)
                .eq(SysRoleBak::getId, roleId)
    public R<SysRole> queryRoleByRoleId(@PathVariable("roleId") Long roleId){
        return R.ok( sysRoleService.getOne(Wrappers.lambdaQuery(SysRole.class)
                .eq(SysRole::getId, roleId)
                .last("LIMIT 1")));
    }
@@ -394,8 +384,7 @@
    @PostMapping("/queryUserByPhoneList")
    public R<List<SysUser>> queryUserByPhoneList(@RequestBody List<String> phoneList){
        List<SysUser> list = userService.list(Wrappers.lambdaQuery(SysUser.class)
                .in(SysUser::getPhonenumber, phoneList)
                .ne(SysUser::getDelFlag,2));
                .in(SysUser::getPhoneNumber, phoneList));
        return R.ok(list);
    }
@@ -408,7 +397,7 @@
    @PostMapping("/queryUserByPhone")
    public R<SysUser>  queryUserByPhone(@RequestBody String phone){
        SysUser user = userService.getOne(Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getPhonenumber, phone)
                .eq(SysUser::getPhoneNumber, phone)
                .last("LIMIT 1"));
        return R.ok(user);
    }
@@ -423,7 +412,7 @@
    @PostMapping("/queryUserByUserName")
    public R<SysUser> queryUserByUserName(@RequestBody String userName){
        SysUser user = userService.getOne(Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getUserName, userName)
                .eq(SysUser::getUserAccount, userName)
                .last("LIMIT 1"));
        return R.ok(user);
    }
@@ -437,24 +426,14 @@
    @PostMapping("/addSysUser")
    @Transactional(rollbackFor = Exception.class)
    public R<Long> addSysUser(@RequestBody SysUser user) {
        if(StringUtils.isEmpty(user.getUserName())){
            user.setUserName(user.getPhonenumber());
        }
        if(!org.springframework.util.StringUtils.hasLength(user.getNickName())){
            user.setNickName(user.getPhonenumber());
        }
//        if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
//            throw new RuntimeException("手机号已开通账号");
//        }
        if (StringUtils.isNotEmpty(user.getUserName()) && !userService.checkUserNameUnique(user)) {
        if (StringUtils.isNotEmpty(user.getUserAccount()) && !userService.checkUserNameUnique(user)) {
            throw new RuntimeException("登录账号已存在");
        }
        user.setCreateBy(SecurityUtils.getUsername());
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        if(null == user.getRoleType()){
            user.setRoleType(1);
        }
        user.setPassWordUpdate(new Date());
        userService.insertUser(user);
        SysUserRole sysUserRole = new SysUserRole();
        sysUserRole.setRoleId(user.getRoleId());
xp-service/xp-account/src/main/java/com/xiaopeng/account/controller/XpAppUserController.java
@@ -1,9 +1,16 @@
package com.xiaopeng.account.controller;
import com.xiaopeng.account.service.IXpAppUserService;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.dto.statistics.UserStatisticsDTO;
import com.xiaopeng.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
/**
 * <p>
@@ -13,8 +20,22 @@
 * @author mitao
 * @since 2026-02-03
 */
@Controller
@RequestMapping("/xp-app-user")
@RestController
@Api(tags = "小程序用户")
public class XpAppUserController {
    @Resource
    private IXpAppUserService xpAppUserService;
    /**
     * 获取用户统计数据(内部接口)
     *
     * @param query 查询参数
     * @return 用户统计数据
     */
    @ApiOperation(value = "获取用户统计数据", tags = {"内部接口"})
    @PostMapping("/inner/appUser/statistics")
    public R<UserStatisticsDTO> getUserStatistics(@RequestBody StatisticsQueryDTO query) {
        return R.ok(xpAppUserService.getUserStatistics(query));
    }
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/mapper/SysRoleMapper.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.api.model.account.SysRole;
import org.apache.ibatis.annotations.Param;
/**
 * <p>
@@ -13,4 +14,11 @@
 */
public interface SysRoleMapper extends BaseMapper<SysRole> {
    /**
     * 统计绑定该角色的用户数
     *
     * @param roleId 角色ID
     * @return 绑定用户数
     */
    Long countBindUsers(@Param("roleId") Long roleId);
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/mapper/SysUserMapper.java
@@ -1,14 +1,14 @@
package com.xiaopeng.account.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.account.model.query.SysUserListQuery;
import com.xiaopeng.account.vo.ChangeUserQuery;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.common.core.web.page.PageInfo;
import org.apache.ibatis.annotations.Param;
import java.util.ArrayList;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.account.vo.ChangeUserQuery;
import com.xiaopeng.account.vo.GetSysUserList;
import com.xiaopeng.api.model.account.SysUser;
import org.apache.ibatis.annotations.Param;
/**
 * 用户表 数据层
@@ -133,7 +133,7 @@
     */
    public SysUser checkEmailUnique(String email);
    List<SysUser> getList(@Param("pageInfo") PageInfo<SysUser> pageInfo, @Param("req") GetSysUserList getSysUserList);
    PageInfo<SysUser> getList(@Param("pageInfo") PageInfo<SysUser> pageInfo, @Param("req") SysUserListQuery sysUserListQuery);
    PageInfo<SysUser> getAllList(@Param("pageInfo") PageInfo<SysUser> pageInfo, @Param("ids") List<Integer> collect);
xp-service/xp-account/src/main/java/com/xiaopeng/account/mapper/XpAppUserMapper.java
@@ -1,7 +1,12 @@
package com.xiaopeng.account.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.api.dto.statistics.DailyCountDTO;
import com.xiaopeng.api.model.account.XpAppUser;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDate;
import java.util.List;
/**
 * <p>
@@ -13,4 +18,21 @@
 */
public interface XpAppUserMapper extends BaseMapper<XpAppUser> {
    /**
     * 统计时间范围内新增用户数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 新增用户数
     */
    Long countNewUsers(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
    /**
     * 获取每日新增用户数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 每日统计列表
     */
    List<DailyCountDTO> getDailyUserCount(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/dto/SysRoleAddDTO.java
New file
@@ -0,0 +1,29 @@
package com.xiaopeng.account.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
 * 角色新增请求
 *
 * @author mitao
 */
@Data
@ApiModel(value = "角色新增请求", description = "新增角色请求参数")
public class SysRoleAddDTO {
    @ApiModelProperty(value = "权限组id", required = true)
    @NotNull(message = "权限组id不能为空")
    private Long groupId;
    @ApiModelProperty(value = "角色名称", required = true)
    @NotBlank(message = "角色名称不能为空")
    private String roleName;
    @ApiModelProperty(value = "备注")
    private String remarks;
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/dto/SysRoleEditDTO.java
New file
@@ -0,0 +1,23 @@
package com.xiaopeng.account.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
/**
 * 角色编辑请求
 *
 * @author mitao
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "角色编辑请求", description = "编辑角色请求参数")
public class SysRoleEditDTO extends SysRoleAddDTO {
    @ApiModelProperty(value = "角色ID", required = true)
    @NotNull(message = "角色ID不能为空")
    private Long id;
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/dto/UserPwdResetDTO.java
New file
@@ -0,0 +1,26 @@
package com.xiaopeng.account.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
 * @author MiTao
 * @date 2026-02-04 17:14
 *
 */
@Data
@ApiModel("用户密码重置DTO")
public class UserPwdResetDTO {
    @NotNull(message = "用户ID不能为空")
    @ApiModelProperty("用户ID")
    private Long userId;
    @NotBlank(message = "新密码不能为空")
    @ApiModelProperty(value = "新密码")
    private String newPassword;
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/query/SysRolePageQuery.java
New file
@@ -0,0 +1,21 @@
package com.xiaopeng.account.model.query;
import com.xiaopeng.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * 角色分页查询请求
 *
 * @author mitao
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "角色分页查询请求", description = "角色分页查询参数")
public class SysRolePageQuery extends BasePage {
    @ApiModelProperty(value = "角色名称")
    private String roleName;
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/query/SysUserListQuery.java
New file
@@ -0,0 +1,27 @@
package com.xiaopeng.account.model.query;
import com.xiaopeng.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author zhibing.pu
 * @Date 2024/8/10 14:10
 */
@Data
@ApiModel
public class SysUserListQuery extends BasePage {
    @ApiModelProperty(value = "用户名称")
    private String nickName;
    @ApiModelProperty(value = "电话号码")
    private String phoneNumber;
    @ApiModelProperty(value = "角色id")
    private Long roleId;
    @ApiModelProperty(value = "账户状态(0=正常,1=禁用)")
    private Integer status;
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/vo/SysMenuVO.java
New file
@@ -0,0 +1,65 @@
package com.xiaopeng.account.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * 菜单VO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "菜单VO", description = "菜单响应对象")
public class SysMenuVO {
    @ApiModelProperty(value = "菜单ID")
    private Long id;
    @ApiModelProperty(value = "菜单名称")
    private String menuName;
    @ApiModelProperty(value = "父菜单ID")
    private Long parentId;
    @ApiModelProperty(value = "显示顺序")
    private Integer orderNum;
    @ApiModelProperty(value = "路由地址")
    private String path;
    @ApiModelProperty(value = "组件路径")
    private String component;
    @ApiModelProperty(value = "路由参数")
    private String query;
    @ApiModelProperty(value = "是否为外链(0是 1否)")
    private Integer isFrame;
    @ApiModelProperty(value = "是否缓存(0缓存 1不缓存)")
    private Integer isCache;
    @ApiModelProperty(value = "菜单类型(M目录 C菜单 F按钮)")
    private String menuType;
    @ApiModelProperty(value = "菜单状态(0显示 1隐藏)")
    private String visible;
    @ApiModelProperty(value = "权限标识")
    private String perms;
    @ApiModelProperty(value = "菜单图标")
    private String icon;
    @ApiModelProperty(value = "备注")
    private String remark;
    @ApiModelProperty(value = "菜单类型 1=管理端 2=运维app")
    private Integer groupType;
    @ApiModelProperty(value = "子菜单")
    private List<SysMenuVO> children;
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/vo/SysRoleDetailVO.java
New file
@@ -0,0 +1,32 @@
package com.xiaopeng.account.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * 角色详情VO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "角色详情VO", description = "角色详情响应对象")
public class SysRoleDetailVO {
    @ApiModelProperty(value = "角色ID")
    private Long id;
    @ApiModelProperty(value = "权限组id")
    private Long groupId;
    @ApiModelProperty(value = "角色名称")
    private String roleName;
    @ApiModelProperty(value = "备注")
    private String remarks;
    @ApiModelProperty(value = "菜单列表")
    private List<SysMenuVO> menus;
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/model/vo/SysRolePageVO.java
New file
@@ -0,0 +1,37 @@
package com.xiaopeng.account.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
 * 角色分页列表VO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "角色分页列表VO", description = "角色分页列表响应对象")
public class SysRolePageVO {
    @ApiModelProperty(value = "角色ID")
    private Long id;
    @ApiModelProperty(value = "权限组id")
    private Long groupId;
    @ApiModelProperty(value = "角色名称")
    private String roleName;
    @ApiModelProperty(value = "备注")
    private String remarks;
    @ApiModelProperty(value = "绑定账号数")
    private Long bindCount;
    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/ISysRoleService.java
@@ -1,7 +1,13 @@
package com.xiaopeng.account.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.account.model.dto.SysRoleAddDTO;
import com.xiaopeng.account.model.dto.SysRoleEditDTO;
import com.xiaopeng.account.model.query.SysRolePageQuery;
import com.xiaopeng.account.model.vo.SysRoleDetailVO;
import com.xiaopeng.account.model.vo.SysRolePageVO;
import com.xiaopeng.api.model.account.SysRole;
import com.xiaopeng.common.core.web.page.PageInfo;
import java.util.Set;
@@ -30,4 +36,50 @@
     * @return 角色权限集合
     */
    Set<String> selectRolePermissionByUserId(Long userId);
    /**
     * 新增角色
     *
     * @param dto 新增角色请求
     */
    void addRole(SysRoleAddDTO dto);
    /**
     * 编辑角色
     *
     * @param dto 编辑角色请求
     */
    void editRole(SysRoleEditDTO dto);
    /**
     * 获取角色详情(含菜单树)
     *
     * @param id 角色ID
     * @return 角色详情
     */
    SysRoleDetailVO getRoleDetail(Long id);
    /**
     * 角色分页列表
     *
     * @param query 分页查询参数
     * @return 分页结果
     */
    PageInfo<SysRolePageVO> pageList(SysRolePageQuery query);
    /**
     * 删除角色(逻辑删除)
     *
     * @param id 角色ID
     */
    void deleteRole(Long id);
    /**
     * 校验角色名称唯一性
     *
     * @param roleName  角色名称
     * @param excludeId 排除的角色ID(编辑时使用)
     * @return true=唯一,false=不唯一
     */
    boolean checkRoleNameUnique(String roleName, Long excludeId);
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/ISysUserService.java
@@ -1,11 +1,11 @@
package com.xiaopeng.account.service;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.account.vo.GetSysUserList;
import com.xiaopeng.account.model.query.SysUserListQuery;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.common.core.web.page.PageInfo;
import java.util.List;
/**
 * 用户 业务层
@@ -124,5 +124,5 @@
     */
    int deleteUserByIds(Long[] userIds);
    PageInfo<SysUser> getList(PageInfo<SysUser> pageInfo, GetSysUserList getSysUserList);
    PageInfo<SysUser> getList(PageInfo<SysUser> pageInfo, SysUserListQuery sysUserListQuery);
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/IXpAppUserService.java
@@ -1,6 +1,8 @@
package com.xiaopeng.account.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.dto.statistics.UserStatisticsDTO;
import com.xiaopeng.api.model.account.XpAppUser;
/**
@@ -13,4 +15,11 @@
 */
public interface IXpAppUserService extends IService<XpAppUser> {
    /**
     * 获取用户统计数据
     *
     * @param query 查询参数
     * @return 用户统计数据
     */
    UserStatisticsDTO getUserStatistics(StatisticsQueryDTO query);
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/SysLoginService.java
@@ -1,20 +1,18 @@
package com.xiaopeng.account.service.impl;
import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
import com.xiaopeng.api.dto.operation.UpdatePasswordDTO;
import com.xiaopeng.account.service.ISysPermissionService;
import com.xiaopeng.account.service.ISysUserService;
import com.xiaopeng.account.service.XpOperationsUserService;
import com.xiaopeng.api.dto.operation.UpdatePasswordDTO;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.account.XpOperationsUser;
import com.xiaopeng.api.vo.account.LoginOperationUser;
import com.xiaopeng.api.model.operation.SysMenu;
import com.xiaopeng.api.vo.account.LoginOperationUser;
import com.xiaopeng.api.vo.account.LoginUser;
import com.xiaopeng.common.core.constant.VerificationCodeConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.constant.Constants;
import com.xiaopeng.common.core.constant.UserConstants;
import com.xiaopeng.common.core.constant.VerificationCodeConstants;
import com.xiaopeng.common.core.enums.UserStatus;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.text.Convert;
@@ -22,11 +20,14 @@
import com.xiaopeng.common.core.utils.ip.IpUtils;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.common.security.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * 登录校验方法
@@ -71,7 +72,7 @@
        LoginUser userInfo = new LoginUser();
        userInfo.setSysUser(user);
        userInfo.setRoles(roles);
        userInfo.setPermissions(menuPermission);
        userInfo.setPermissions(menuPermission.stream().map(SysMenu::getPath).collect(Collectors.toSet()));
        if (StringUtils.isNull(user)) {
            recordLogService.recordLogininfor(request, null, username, Constants.LOGIN_FAIL_STATUS, "登录用户不存在");
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/SysPermissionServiceImpl.java
@@ -4,19 +4,16 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.xiaopeng.account.service.ISysGroupMenuService;
import com.xiaopeng.account.service.ISysPermissionService;
import com.xiaopeng.account.service.ISysRoleService;
import com.xiaopeng.account.service.ISysUserRoleService;
import com.xiaopeng.api.feign.operation.RemoteMenuService;
import com.xiaopeng.api.feignClient.operation.SysMenuClient;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.account.SysUserRole;
import com.xiaopeng.api.model.operation.SysMenu;
import com.xiaopeng.common.core.constant.SecurityConstants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -42,7 +39,7 @@
    private ISysGroupMenuService groupMenuService;
    @Autowired
    private RemoteMenuService remoteMenuService;
    private SysMenuClient sysMenuClient;
    /**
     * 获取角色数据权限
@@ -104,7 +101,7 @@
            }
            // 4. 通过 Feign 调用 operation 服务,根据 menuId 列表获取菜单列表
            R<List<SysMenu>> result = remoteMenuService.getMenusByMenuIds(menuIds);
            R<List<SysMenu>> result = sysMenuClient.getMenusByMenuIds(menuIds);
            if (R.isSuccess(result) && result.getData() != null) {
                menus = result.getData();
            } else {
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/SysRoleServiceImpl.java
@@ -1,18 +1,35 @@
package com.xiaopeng.account.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.account.mapper.SysRoleMapper;
import com.xiaopeng.account.model.dto.SysRoleAddDTO;
import com.xiaopeng.account.model.dto.SysRoleEditDTO;
import com.xiaopeng.account.model.query.SysRolePageQuery;
import com.xiaopeng.account.model.vo.SysMenuVO;
import com.xiaopeng.account.model.vo.SysRoleDetailVO;
import com.xiaopeng.account.model.vo.SysRolePageVO;
import com.xiaopeng.account.service.ISysGroupMenuService;
import com.xiaopeng.account.service.ISysRoleService;
import com.xiaopeng.account.service.ISysUserRoleService;
import com.xiaopeng.api.feignClient.operation.SysMenuClient;
import com.xiaopeng.api.model.account.SysRole;
import com.xiaopeng.api.model.account.SysUserRole;
import com.xiaopeng.api.model.operation.SysMenu;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.xiaopeng.common.core.web.page.PageInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -22,11 +39,16 @@
 * @author mitao
 * @since 2026-02-03
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Lazy)
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
    @Autowired
    private ISysUserRoleService userRoleService;
    private final ISysUserRoleService userRoleService;
    private final ISysGroupMenuService groupMenuService;
    private final SysMenuClient sysMenuClient;
    /**
     * 根据角色ID查询权限组ID
@@ -70,4 +92,244 @@
        return roles;
    }
    /**
     * 新增角色
     *
     * @param dto 新增角色请求
     */
    @Override
    public void addRole(SysRoleAddDTO dto) {
        // 校验角色名称唯一性
        if (!checkRoleNameUnique(dto.getRoleName(), null)) {
            throw new ServiceException("角色名称已存在");
        }
        // 创建角色实体
        SysRole role = new SysRole();
        role.setGroupId(dto.getGroupId());
        role.setRoleName(dto.getRoleName());
        role.setRemarks(dto.getRemarks());
        // 保存角色
        this.save(role);
    }
    /**
     * 编辑角色
     *
     * @param dto 编辑角色请求
     */
    @Override
    public void editRole(SysRoleEditDTO dto) {
        // 校验角色是否存在
        SysRole existRole = this.getById(dto.getId());
        if (existRole == null) {
            throw new ServiceException("角色不存在");
        }
        // 校验角色名称唯一性(排除自身)
        if (!checkRoleNameUnique(dto.getRoleName(), dto.getId())) {
            throw new ServiceException("角色名称已存在");
        }
        // 更新角色信息
        existRole.setGroupId(dto.getGroupId());
        existRole.setRoleName(dto.getRoleName());
        existRole.setRemarks(dto.getRemarks());
        this.updateById(existRole);
    }
    /**
     * 获取角色详情(含菜单树)
     *
     * @param id 角色ID
     * @return 角色详情
     */
    @Override
    public SysRoleDetailVO getRoleDetail(Long id) {
        // 查询角色基础信息
        SysRole role = this.getById(id);
        if (role == null) {
            throw new ServiceException("角色不存在");
        }
        SysRoleDetailVO vo =  BeanUtil.toBean(role, SysRoleDetailVO.class);
        // 根据权限组ID获取菜单ID列表
        List<Long> menuIds = groupMenuService.selectMenuIdsByGroupId(role.getGroupId());
        if (CollectionUtils.isEmpty(menuIds)) {
            vo.setMenus(new ArrayList<>());
            return vo;
        }
        // 通过Feign调用获取菜单详情
        try {
            R<List<SysMenu>> result = sysMenuClient.getMenusByMenuIds(menuIds);
            if (result != null && result.getData() != null) {
                List<SysMenu> menus = result.getData();
                // 构建菜单树
                vo.setMenus(buildMenuTree(menus));
            } else {
                vo.setMenus(new ArrayList<>());
            }
        } catch (Exception e) {
            log.error("获取菜单列表失败: {}", e.getMessage());
            vo.setMenus(new ArrayList<>());
        }
        return vo;
    }
    /**
     * 角色分页列表
     *
     * @param query 分页查询参数
     * @return 分页结果
     */
    @Override
    public PageInfo<SysRolePageVO> pageList(SysRolePageQuery query) {
        // 构建查询条件
        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(query.getRoleName())) {
            wrapper.like(SysRole::getRoleName, query.getRoleName());
        }
        wrapper.orderByDesc(SysRole::getCreateTime);
        // 分页查询
        PageInfo<SysRole> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        PageInfo<SysRole> rolePage = this.page(pageInfo, wrapper);
        // 转换为VO
        PageInfo<SysRolePageVO> resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize());
        resultPage.setTotal(rolePage.getTotal());
        List<SysRolePageVO> voList = rolePage.getRecords().stream().map(role -> {
            SysRolePageVO vo = new SysRolePageVO();
            vo.setId(role.getId());
            vo.setGroupId(role.getGroupId());
            vo.setRoleName(role.getRoleName());
            vo.setRemarks(role.getRemarks());
            vo.setCreateTime(role.getCreateTime());
            // 统计绑定用户数
            Long bindCount = baseMapper.countBindUsers(role.getId());
            vo.setBindCount(bindCount);
            return vo;
        }).collect(Collectors.toList());
        resultPage.setRecords(voList);
        return resultPage;
    }
    /**
     * 删除角色(逻辑删除)
     *
     * @param id 角色ID
     */
    @Override
    public void deleteRole(Long id) {
        // 校验角色是否存在
        SysRole role = this.getById(id);
        if (role == null) {
            throw new ServiceException("角色不存在");
        }
        // 检查是否有用户绑定该角色
        Long bindCount = baseMapper.countBindUsers(id);
        if (bindCount != null && bindCount > 0) {
            throw new ServiceException("该角色已绑定用户,无法删除");
        }
        // 逻辑删除
        this.removeById(id);
    }
    /**
     * 校验角色名称唯一性
     *
     * @param roleName  角色名称
     * @param excludeId 排除的角色ID(编辑时使用)
     * @return true=唯一,false=不唯一
     */
    @Override
    public boolean checkRoleNameUnique(String roleName, Long excludeId) {
        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysRole::getRoleName, roleName);
        if (excludeId != null) {
            wrapper.ne(SysRole::getId, excludeId);
        }
        return this.count(wrapper) == 0;
    }
    /**
     * 构建菜单树
     *
     * @param menus 菜单列表
     * @return 菜单树
     */
    private List<SysMenuVO> buildMenuTree(List<SysMenu> menus) {
        if (CollectionUtils.isEmpty(menus)) {
            return new ArrayList<>();
        }
        Map<Long, SysMenuVO> menuMap = new HashMap<>();
        List<SysMenuVO> rootMenus = new ArrayList<>();
        // 转换为VO并建立映射
        for (SysMenu menu : menus) {
            SysMenuVO vo = convertToMenuVO(menu);
            vo.setChildren(new ArrayList<>());
            menuMap.put(vo.getId(), vo);
        }
        // 构建树结构
        for (SysMenuVO vo : menuMap.values()) {
            if (vo.getParentId() == null || vo.getParentId() == 0) {
                rootMenus.add(vo);
            } else {
                SysMenuVO parent = menuMap.get(vo.getParentId());
                if (parent != null) {
                    parent.getChildren().add(vo);
                } else {
                    // 父节点不在列表中,作为根节点处理
                    rootMenus.add(vo);
                }
            }
        }
        // 按orderNum排序
        sortMenuTree(rootMenus);
        return rootMenus;
    }
    /**
     * 转换为菜单VO
     *
     * @param menu 菜单实体
     * @return 菜单VO
     */
    private SysMenuVO convertToMenuVO(SysMenu menu) {
        SysMenuVO vo = new SysMenuVO();
        BeanUtils.copyProperties(menu, vo);
        return vo;
    }
    /**
     * 递归排序菜单树
     *
     * @param menus 菜单列表
     */
    private void sortMenuTree(List<SysMenuVO> menus) {
        if (CollectionUtils.isEmpty(menus)) {
            return;
        }
        menus.sort(Comparator.comparing(SysMenuVO::getOrderNum, Comparator.nullsLast(Comparator.naturalOrder())));
        for (SysMenuVO menu : menus) {
            if (!CollectionUtils.isEmpty(menu.getChildren())) {
                sortMenuTree(menu.getChildren());
            }
        }
    }
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/SysUserServiceImpl.java
@@ -9,7 +9,7 @@
import com.xiaopeng.account.mapper.SysUserMapper;
import com.xiaopeng.account.mapper.SysUserRoleMapper;
import com.xiaopeng.account.service.ISysUserService;
import com.xiaopeng.account.vo.GetSysUserList;
import com.xiaopeng.account.model.query.SysUserListQuery;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.account.SysUserRole;
import org.slf4j.Logger;
@@ -19,8 +19,8 @@
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
 * 用户 业务层处理
@@ -164,9 +164,7 @@
//        insertUserPost(user);
//        // 新增用户与角色管理
//        insertUserRole(user);
        user.setCreateTime(new Date());
        user.setCreateBy(tokenService.getLoginUser().getUsername());
        user.setUpdateTime(new Date());
        user.setUpdateBy(tokenService.getLoginUser().getUsername());
        return userMapper.insert(user);
    }
@@ -248,15 +246,17 @@
    @Transactional(rollbackFor = Exception.class)
    public int deleteUserByIds(Long[] userIds) {
        for (Long userId : userIds) {
            checkUserAllowed(new SysUser(userId));
            SysUser sysUser = new SysUser();
            sysUser.setId(userId);
            checkUserAllowed(sysUser);
        }
        return userMapper.deleteUserByIds(userIds);
    }
    @Override
    public PageInfo<SysUser> getList(PageInfo<SysUser> pageInfo, GetSysUserList getSysUserList) {
        return null;
    public PageInfo<SysUser> getList(PageInfo<SysUser> pageInfo, SysUserListQuery sysUserListQuery) {
        return userMapper.getList(pageInfo, sysUserListQuery);
    }
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/service/impl/XpAppUserServiceImpl.java
@@ -3,8 +3,15 @@
import com.xiaopeng.account.mapper.XpAppUserMapper;
import com.xiaopeng.account.service.IXpAppUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.dto.statistics.DailyCountDTO;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.dto.statistics.UserStatisticsDTO;
import com.xiaopeng.api.model.account.XpAppUser;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
/**
 * <p>
@@ -17,4 +24,27 @@
@Service
public class XpAppUserServiceImpl extends ServiceImpl<XpAppUserMapper, XpAppUser> implements IXpAppUserService {
    @Override
    public UserStatisticsDTO getUserStatistics(StatisticsQueryDTO query) {
        UserStatisticsDTO dto = new UserStatisticsDTO();
        // 1. 查询用户总数
        dto.setTotalCount(baseMapper.selectCount(null));
        // 2. 查询时间范围内新增用户数
        if (query.getStartDate() != null && query.getEndDate() != null) {
            dto.setNewCount(baseMapper.countNewUsers(query.getStartDate(), query.getEndDate()));
        } else {
            dto.setNewCount(0L);
        }
        // 3. 查询近N天每日新增用户数(默认30天)
        Integer trendDays = query.getTrendDays() != null ? query.getTrendDays() : 30;
        LocalDate endDate = LocalDate.now();
        LocalDate startDate = endDate.minusDays(trendDays - 1);
        List<DailyCountDTO> dailyCountList = baseMapper.getDailyUserCount(startDate, endDate);
        dto.setDailyCountList(dailyCountList != null ? dailyCountList : new ArrayList<>());
        return dto;
    }
}
xp-service/xp-account/src/main/java/com/xiaopeng/account/vo/GetSysUserList.java
File was deleted
xp-service/xp-account/src/main/resources/mapper/account/SysRoleMapper.xml
@@ -2,4 +2,13 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaopeng.account.mapper.SysRoleMapper">
    <!-- 统计绑定该角色的用户数 -->
    <select id="countBindUsers" resultType="java.lang.Long">
        SELECT COUNT(*)
        FROM sys_user_role sur
                 left join sys_user su on su.id = sur.user_id
        WHERE role_id = #{roleId}
          and su.is_deleted = 0
    </select>
</mapper>
xp-service/xp-account/src/main/resources/mapper/account/SysUserMapper.xml
@@ -124,27 +124,21 @@
    <select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
        select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1
    </select>
    <select id="getList" resultMap="SysUserResult">
        select * from sys_user where 1=1
        <if test="req.search !=null and req.search !=''">
            and (phonenumber like concat("%", #{req.search},"%") or nick_name like concat("%", #{req.search},"%"))
    <select id="getList" resultMap="SysUserResult">
        select su.*,sr.role_name from sys_user su
        left join sys_user_role sur on su.id= sur.user_id
        left join sys_role sr on sr.id = sur.role_id where 1=1
        <if test="req.nickname !=null and req.nickname !=''">
            and nick_name like concat("%", #{req.nickname},"%")
        </if>
        <if test="null != req.siteId">
            and user_id in (select user_id from t_user_site where site_id = #{req.siteId})
            and user_id in (select user_id from sys_user_role where role_id in (select role_id from t_role_site where site_id = #{req.siteId}))
        <if test="null != req.roleId">
            and sur.role_id = #{req.roleId}
        </if>
        <if test="null != req.roleIds and req.roleIds.size() > 0">
            and user_id in (select user_id from sys_user_role where role_id in
            <foreach collection="req.roleIds" item="item" index="index" open="(" separator="," close=")">
                #{item}
            </foreach>
            )
        </if>
        <if test="null != req.status">
            and status = #{req.status}
            and su.status = #{req.status}
        </if>
        and del_flag = '0' and roleType =1
        order by create_time desc
        and su.is_deleted = 0
        order by su.create_time desc
    </select>
    <select id="getAllList" resultType="com.ruoyi.system.api.domain.SysUser">
        select *,user_id userId,nick_name userName ,create_time createTime ,login_date loginDate from sys_user where roleType=3 and user_id in
xp-service/xp-account/src/main/resources/mapper/account/XpAppUserMapper.xml
@@ -7,4 +7,24 @@
        id, nick_name, avatar, phone_number, gender, open_id, is_deleted, create_time, update_time, create_by, update_by
    </sql>
    <!-- 统计时间范围内新增用户数 -->
    <select id="countNewUsers" resultType="java.lang.Long">
        SELECT COUNT(*)
        FROM xp_app_user
        WHERE is_deleted = 0
          AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
    </select>
    <!-- 获取每日新增用户数 -->
    <select id="getDailyUserCount" resultType="com.xiaopeng.api.dto.statistics.DailyCountDTO">
        SELECT
            DATE_FORMAT(create_time, '%Y-%m-%d') as date,
            COUNT(*) as count
        FROM xp_app_user
        WHERE is_deleted = 0
          AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
        GROUP BY DATE(create_time)
        ORDER BY date ASC
    </select>
</mapper>
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/controller/IndexController.java
New file
@@ -0,0 +1,37 @@
package com.xiaopeng.maintenance.controller;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.maintenance.dto.IndexDataQuery;
import com.xiaopeng.maintenance.service.IndexDataService;
import com.xiaopeng.maintenance.vo.IndexDataVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * 首页 Controller
 *
 * @author mitao
 */
@Slf4j
@RestController
@Api(tags = "管理后台-首页")
public class IndexController {
    @Resource
    private IndexDataService indexDataService;
    @ApiOperation(value = "首页数据统计", tags = {"管理后台-首页"})
    @PostMapping("/base/v1/indexData")
    public R<IndexDataVO> indexData(@RequestBody(required = false) IndexDataQuery query) {
        if (query == null) {
            query = new IndexDataQuery();
        }
        return R.ok(indexDataService.getIndexData(query));
    }
}
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/dto/IndexDataQuery.java
New file
@@ -0,0 +1,30 @@
package com.xiaopeng.maintenance.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
/**
 * 首页数据查询参数
 *
 * @author mitao
 */
@Data
@ApiModel(value = "首页数据查询参数")
public class IndexDataQuery {
    @ApiModelProperty(value = "今日运行情况-查询日期(默认当天)")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate runningDate;
    @ApiModelProperty(value = "运营增长数据-开始日期(默认当天)")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate growthStartDate;
    @ApiModelProperty(value = "运营增长数据-结束日期(默认当天)")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate growthEndDate;
}
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/mapper/XpFleetMapper.java
@@ -1,7 +1,12 @@
package com.xiaopeng.maintenance.mapper;
import com.xiaopeng.api.dto.statistics.DailyCountDTO;
import com.xiaopeng.api.model.maintenance.XpFleet;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDate;
import java.util.List;
/**
 * <p>
@@ -13,4 +18,21 @@
 */
public interface XpFleetMapper extends BaseMapper<XpFleet> {
    /**
     * 统计时间范围内新增车队数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 新增车队数
     */
    Long countNewFleets(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
    /**
     * 获取每日新增车队数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 每日统计列表
     */
    List<DailyCountDTO> getDailyFleetCount(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
}
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/mapper/XpVehicleMapper.java
@@ -1,7 +1,12 @@
package com.xiaopeng.maintenance.mapper;
import com.xiaopeng.api.dto.statistics.DailyCountDTO;
import com.xiaopeng.api.model.maintenance.XpVehicle;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDate;
import java.util.List;
/**
 * <p>
@@ -13,4 +18,35 @@
 */
public interface XpVehicleMapper extends BaseMapper<XpVehicle> {
    /**
     * 统计在线车辆数(operatingStatus=1 运营中)
     *
     * @return 在线车辆数
     */
    Long countOnlineVehicles();
    /**
     * 统计运行车辆数(operatingStatus=1 运营中)
     *
     * @return 运行车辆数
     */
    Long countRunningVehicles();
    /**
     * 统计时间范围内新增车辆数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 新增车辆数
     */
    Long countNewVehicles(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
    /**
     * 获取每日新增车辆数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 每日统计列表
     */
    List<DailyCountDTO> getDailyVehicleCount(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
}
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/service/IndexDataService.java
New file
@@ -0,0 +1,20 @@
package com.xiaopeng.maintenance.service;
import com.xiaopeng.maintenance.dto.IndexDataQuery;
import com.xiaopeng.maintenance.vo.IndexDataVO;
/**
 * 首页数据统计服务
 *
 * @author mitao
 */
public interface IndexDataService {
    /**
     * 获取首页统计数据
     *
     * @param query 查询参数
     * @return 首页统计数据
     */
    IndexDataVO getIndexData(IndexDataQuery query);
}
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/service/impl/IndexDataServiceImpl.java
New file
@@ -0,0 +1,193 @@
package com.xiaopeng.maintenance.service.impl;
import com.xiaopeng.api.dto.statistics.*;
import com.xiaopeng.api.feignClient.account.XpAppUserClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.maintenance.dto.IndexDataQuery;
import com.xiaopeng.maintenance.mapper.XpFleetMapper;
import com.xiaopeng.maintenance.mapper.XpVehicleMapper;
import com.xiaopeng.maintenance.service.IndexDataService;
import com.xiaopeng.maintenance.vo.IndexDataVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 首页数据统计服务实现
 *
 * @author mitao
 */
@Slf4j
@Service
public class IndexDataServiceImpl implements IndexDataService {
    @Resource
    private XpVehicleMapper xpVehicleMapper;
    @Resource
    private XpFleetMapper xpFleetMapper;
    @Resource
    private XpAppUserClient xpAppUserClient;
    @Resource
    private XpOrderClient xpOrderClient;
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    @Override
    public IndexDataVO getIndexData(IndexDataQuery query) {
        IndexDataVO vo = new IndexDataVO();
        // 1. 处理默认参数
        LocalDate today = LocalDate.now();
        LocalDate runningDate = query.getRunningDate() != null ? query.getRunningDate() : today;
        LocalDate growthStartDate = query.getGrowthStartDate() != null ? query.getGrowthStartDate() : today;
        LocalDate growthEndDate = query.getGrowthEndDate() != null ? query.getGrowthEndDate() : today;
        // 2. 构建今日运行情况查询参数
        StatisticsQueryDTO todayQuery = new StatisticsQueryDTO();
        todayQuery.setStartDate(runningDate);
        todayQuery.setEndDate(runningDate);
        todayQuery.setTrendDays(30);
        // 3. 调用Feign获取今日用户统计数据
        UserStatisticsDTO todayUserStats = getUserStatistics(todayQuery);
        // 4. 调用Feign获取今日订单统计数据
        OrderStatisticsDTO todayOrderStats = getOrderStatistics(todayQuery);
        // 5. 本地查询今日车辆统计数据
        Long todayNewVehicles = xpVehicleMapper.countNewVehicles(runningDate, runningDate);
        // 6. 组装今日运行情况
        vo.setNewUserCount(todayUserStats.getNewCount() != null ? todayUserStats.getNewCount() : 0L);
        vo.setVehicleCount(todayNewVehicles != null ? todayNewVehicles : 0L);
        vo.setOrderCount(todayOrderStats.getNewCount() != null ? todayOrderStats.getNewCount() : 0L);
        // 7. 查询运营总数据
        StatisticsQueryDTO totalQuery = new StatisticsQueryDTO();
        totalQuery.setTrendDays(30);
        UserStatisticsDTO totalUserStats = getUserStatistics(totalQuery);
        OrderStatisticsDTO totalOrderStats = getOrderStatistics(totalQuery);
        Long totalVehicles = xpVehicleMapper.selectCount(null);
        Long totalFleets = xpFleetMapper.selectCount(null);
        // 8. 组装运营总数据
        vo.setTotalUserCount(totalUserStats.getTotalCount() != null ? totalUserStats.getTotalCount() : 0L);
        vo.setFleetCount(totalFleets != null ? totalFleets : 0L);
        vo.setTotalVehicleCount(totalVehicles != null ? totalVehicles : 0L);
        vo.setTotalOrderCount(totalOrderStats.getTotalCount() != null ? totalOrderStats.getTotalCount() : 0L);
        // 总交易金额和总收入(pay_money累加)
        BigDecimal totalMoney = totalOrderStats.getTotalMoney() != null ? totalOrderStats.getTotalMoney() : BigDecimal.ZERO;
        vo.setTotalMoney(totalMoney.longValue());
        vo.setTotalIncome(totalMoney.longValue());
        // 9. 查询运营增长数据
        StatisticsQueryDTO growthQuery = new StatisticsQueryDTO();
        growthQuery.setStartDate(growthStartDate);
        growthQuery.setEndDate(growthEndDate);
        growthQuery.setTrendDays(30);
        UserStatisticsDTO growthUserStats = getUserStatistics(growthQuery);
        OrderStatisticsDTO growthOrderStats = getOrderStatistics(growthQuery);
        Long growthVehicles = xpVehicleMapper.countNewVehicles(growthStartDate, growthEndDate);
        // 10. 组装运营增长数据
        vo.setUserGrowthCount(growthUserStats.getNewCount() != null ? growthUserStats.getNewCount() : 0L);
        vo.setVehicleGrowthCount(growthVehicles != null ? growthVehicles : 0L);
        vo.setOrderGrowthCount(growthOrderStats.getNewCount() != null ? growthOrderStats.getNewCount() : 0L);
        // 11. 组装运营总趋势(近30天)
        LocalDate trendEndDate = LocalDate.now();
        LocalDate trendStartDate = trendEndDate.minusDays(29);
        // 生成日期列表
        List<String> dateList = new ArrayList<>();
        for (LocalDate date = trendStartDate; !date.isAfter(trendEndDate); date = date.plusDays(1)) {
            dateList.add(date.format(DATE_FORMATTER));
        }
        // 获取用户趋势数据
        StatisticsQueryDTO trendQuery = new StatisticsQueryDTO();
        trendQuery.setStartDate(trendStartDate);
        trendQuery.setEndDate(trendEndDate);
        trendQuery.setTrendDays(30);
        UserStatisticsDTO trendUserStats = getUserStatistics(trendQuery);
        OrderStatisticsDTO trendOrderStats = getOrderStatistics(trendQuery);
        List<DailyCountDTO> userDailyList = trendUserStats.getDailyCountList() != null ? trendUserStats.getDailyCountList() : new ArrayList<>();
        List<DailyCountDTO> orderDailyList = trendOrderStats.getDailyCountList() != null ? trendOrderStats.getDailyCountList() : new ArrayList<>();
        // 转换为Map便于查找
        Map<String, Long> userDailyMap = userDailyList.stream()
                .collect(Collectors.toMap(DailyCountDTO::getDate, DailyCountDTO::getCount, (a, b) -> a));
        Map<String, Long> orderDailyMap = orderDailyList.stream()
                .collect(Collectors.toMap(DailyCountDTO::getDate, DailyCountDTO::getCount, (a, b) -> a));
        // 填充趋势数据(确保每天都有数据,没有的填0)
        List<Long> trendUserList = new ArrayList<>();
        List<Long> trendOrderList = new ArrayList<>();
        for (String date : dateList) {
            trendUserList.add(userDailyMap.getOrDefault(date, 0L));
            trendOrderList.add(orderDailyMap.getOrDefault(date, 0L));
        }
        vo.setUserCountList(trendUserList);
        vo.setOrderCountList(trendOrderList);
        return vo;
    }
    /**
     * 获取用户统计数据(带降级处理)
     */
    private UserStatisticsDTO getUserStatistics(StatisticsQueryDTO query) {
        try {
            R<UserStatisticsDTO> result = xpAppUserClient.getUserStatistics(query);
            if (result != null && result.getData() != null) {
                return result.getData();
            }
        } catch (Exception e) {
            log.error("获取用户统计数据失败: {}", e.getMessage());
        }
        // 返回默认空数据
        UserStatisticsDTO dto = new UserStatisticsDTO();
        dto.setTotalCount(0L);
        dto.setNewCount(0L);
        dto.setDailyCountList(new ArrayList<>());
        return dto;
    }
    /**
     * 获取订单统计数据(带降级处理)
     */
    private OrderStatisticsDTO getOrderStatistics(StatisticsQueryDTO query) {
        try {
            R<OrderStatisticsDTO> result = xpOrderClient.getOrderStatistics(query);
            if (result != null && result.getData() != null) {
                return result.getData();
            }
        } catch (Exception e) {
            log.error("获取订单统计数据失败: {}", e.getMessage());
        }
        // 返回默认空数据
        OrderStatisticsDTO dto = new OrderStatisticsDTO();
        dto.setTotalCount(0L);
        dto.setNewCount(0L);
        dto.setTotalMoney(BigDecimal.ZERO);
        dto.setTotalIncome(BigDecimal.ZERO);
        dto.setDailyCountList(new ArrayList<>());
        return dto;
    }
}
xp-service/xp-maintenance/src/main/java/com/xiaopeng/maintenance/vo/IndexDataVO.java
New file
@@ -0,0 +1,67 @@
package com.xiaopeng.maintenance.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * 首页数据统计VO
 *
 * @author mitao
 */
@Data
@ApiModel(value = "首页数据统计VO")
public class IndexDataVO {
    // ==================== 今日运行情况 ====================
    @ApiModelProperty(value = "今日运行情况-新增用户数")
    private Long newUserCount;
    @ApiModelProperty(value = "今日运行情况-新增车辆数量")
    private Long vehicleCount;
    @ApiModelProperty(value = "今日运行情况-新增订单数量")
    private Long orderCount;
    // ==================== 运营总数据 ====================
    @ApiModelProperty(value = "运营总数据-用户总数")
    private Long totalUserCount;
    @ApiModelProperty(value = "运营总数据-车队数量")
    private Long fleetCount;
    @ApiModelProperty(value = "运营总数据-车辆数量")
    private Long totalVehicleCount;
    @ApiModelProperty(value = "运营总数据-订单数量")
    private Long totalOrderCount;
    @ApiModelProperty(value = "运营总数据-总交易金额")
    private Long totalMoney;
    @ApiModelProperty(value = "运营总数据-总收入")
    private Long totalIncome;
    // ==================== 运营数据增长 ====================
    @ApiModelProperty(value = "运营数据增长-用户增长数")
    private Long userGrowthCount;
    @ApiModelProperty(value = "运营数据增长-车辆增长数")
    private Long vehicleGrowthCount;
    @ApiModelProperty(value = "运营数据增长-订单增长数")
    private Long orderGrowthCount;
    // ==================== 运营总趋势(近30天) ====================
    @ApiModelProperty(value = "运营总趋势-订单趋势图(近30天每日订单数)")
    private List<Long> orderCountList;
    @ApiModelProperty(value = "运营总趋势-用户注册趋势图(近30天每日用户注册数)")
    private List<Long> userCountList;
}
xp-service/xp-maintenance/src/main/resources/mapper/maintenance/SysMenuClient.xml
xp-service/xp-maintenance/src/main/resources/mapper/maintenance/XpFleetMapper.xml
@@ -2,4 +2,24 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaopeng.maintenance.mapper.XpFleetMapper">
    <!-- 统计时间范围内新增车队数 -->
    <select id="countNewFleets" resultType="java.lang.Long">
        SELECT COUNT(*)
        FROM xp_fleet
        WHERE is_deleted = 0
          AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
    </select>
    <!-- 获取每日新增车队数 -->
    <select id="getDailyFleetCount" resultType="com.xiaopeng.api.dto.statistics.DailyCountDTO">
        SELECT
            DATE_FORMAT(create_time, '%Y-%m-%d') as date,
            COUNT(*) as count
        FROM xp_fleet
        WHERE is_deleted = 0
          AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
        GROUP BY DATE(create_time)
        ORDER BY date ASC
    </select>
</mapper>
xp-service/xp-maintenance/src/main/resources/mapper/maintenance/XpVehicleMapper.xml
@@ -2,4 +2,40 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaopeng.maintenance.mapper.XpVehicleMapper">
    <!-- 统计在线车辆数(operatingStatus=1 运营中) -->
    <select id="countOnlineVehicles" resultType="java.lang.Long">
        SELECT COUNT(*)
        FROM xp_vehicle
        WHERE is_deleted = 0
          AND operating_status = 1
    </select>
    <!-- 统计运行车辆数(operatingStatus=1 运营中) -->
    <select id="countRunningVehicles" resultType="java.lang.Long">
        SELECT COUNT(*)
        FROM xp_vehicle
        WHERE is_deleted = 0
          AND operating_status = 1
    </select>
    <!-- 统计时间范围内新增车辆数 -->
    <select id="countNewVehicles" resultType="java.lang.Long">
        SELECT COUNT(*)
        FROM xp_vehicle
        WHERE is_deleted = 0
          AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
    </select>
    <!-- 获取每日新增车辆数 -->
    <select id="getDailyVehicleCount" resultType="com.xiaopeng.api.dto.statistics.DailyCountDTO">
        SELECT
            DATE_FORMAT(create_time, '%Y-%m-%d') as date,
            COUNT(*) as count
        FROM xp_vehicle
        WHERE is_deleted = 0
          AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
        GROUP BY DATE(create_time)
        ORDER BY date ASC
    </select>
</mapper>
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/IndexController.java
File was deleted
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/XpInvitationCodeController.java
@@ -1,9 +1,50 @@
package com.xiaopeng.operation.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.operation.XpInvitationCode;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.DateUtils;
import com.xiaopeng.common.core.utils.WebUtils;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.log.annotation.Log;
import com.xiaopeng.common.log.enums.BusinessType;
import com.xiaopeng.operation.exportExcle.XpInvitationCodeExportExcel;
import com.xiaopeng.operation.exportExcle.XpWhitelistExportExcel;
import com.xiaopeng.operation.importExcel.InvitationCodeImportExcel;
import com.xiaopeng.operation.query.InvitationPageListQuery;
import com.xiaopeng.operation.query.WhitelistPageListQuery;
import com.xiaopeng.operation.service.IXpInvitationCodeService;
import com.xiaopeng.operation.service.IXpWhitelistService;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -13,8 +54,248 @@
 * @author mitao
 * @since 2026-02-03
 */
@Controller
@RequestMapping("/xp-invitation-code")
@RestController
@Api(tags = "邀请码管理")
@Slf4j
@RequestMapping("/api/v1/invitation")
public class XpInvitationCodeController {
    @Resource
    private IXpInvitationCodeService xpInvitationCodeService;
    @Resource
    private IXpWhitelistService xpWhitelistService;
    @ApiOperation(value = "邀请码管理分页列表",response = XpInvitationCode.class)
    @PostMapping("/pageList")
    public AjaxResult<PageInfo<XpInvitationCode>> pageList(@RequestBody InvitationPageListQuery query) {
        PageInfo<XpInvitationCode> pageList = xpInvitationCodeService.pageList(query);
        return AjaxResult.ok(pageList);
    }
    /**
     * 生成邀请码
     */
    @PutMapping("/add")
    @ResponseBody
    @ApiOperation(value = "生成邀请码")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "手机号 多个逗号隔开", name = "phones", dataType = "String", required = true)
    })
    @Transactional
    public AjaxResult add(@RequestParam("phones") String phones){
        Map<String, Long> codeMap = xpInvitationCodeService.list().stream()
                .collect(Collectors.toMap(XpInvitationCode::getInvitationCode, XpInvitationCode::getId));
        if (StringUtils.hasLength(phones)){
            List<XpInvitationCode> addInvitationCodeList = new ArrayList<>();
            List<XpWhitelist> addWhitelistList = new ArrayList<>();
            for (String phone : Arrays.asList(phones.split(","))) {
                XpInvitationCode xpInvitationCode = new XpInvitationCode();
                // 生成随机六位数邀请码包含数字和大写字母
                String code = generateInviteCode();
                // 若生成出已存在的邀请码 重新生成
                Long l = codeMap.get(code);
                while (l!=null){
                    code = generateInviteCode();
                    l = codeMap.get(code);
                }
                xpInvitationCode.setInvitationCode(code);
                xpInvitationCode.setBindAccount(phone);
                xpInvitationCode.setBindTime(LocalDateTime.now());
                xpInvitationCode.setStatus(2);
                addInvitationCodeList.add(xpInvitationCode);
                XpWhitelist xpWhitelist = new XpWhitelist();
                xpWhitelist.setAccount(phone);
                xpWhitelist.setStatus(true);
                xpWhitelist.setIsPriorityDispatch(false);
                xpWhitelist.setWhitelistSource(2);
                addWhitelistList.add(xpWhitelist);
            }
            xpInvitationCodeService.saveBatch(addInvitationCodeList);
            xpWhitelistService.saveBatch(addWhitelistList);
            return AjaxResult.success();
        }else {
            return AjaxResult.error("请输入邀请手机号");
        }
    }
    /**
     * 解绑
     */
    @PutMapping("/unbind")
    @ResponseBody
    @ApiOperation(value = "解绑")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "id", name = "id", dataType = "Long", required = true)
    })
    public AjaxResult unbind(@RequestParam("id") Long id){
        UpdateWrapper<XpInvitationCode> xpInvitationCodeUpdateWrapper = new UpdateWrapper<>();
        xpInvitationCodeUpdateWrapper.eq("id",id);
        xpInvitationCodeUpdateWrapper.set("status",3);
        xpInvitationCodeUpdateWrapper.set("bind_account", null);
        xpInvitationCodeService.update(xpInvitationCodeUpdateWrapper);
        return AjaxResult.success();
    }
    @ApiOperation(value = "点位导入模板下载")
    @GetMapping("/import-template")
    public void importTemplate() {
        List<InvitationCodeImportExcel> invitationCodeImportExcels = new ArrayList<>();
        InvitationCodeImportExcel invitationCodeImportExcel = new InvitationCodeImportExcel();
        invitationCodeImportExcels.add(invitationCodeImportExcel);
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), InvitationCodeImportExcel.class, invitationCodeImportExcels);
        HttpServletResponse response = WebUtils.response();
        ServletOutputStream outputStream = null;
        try {
            String fileName = URLEncoder.encode("邀请码导入模板.xls", "utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("邀请码导入模板下载失败!");
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @Log(title = "邀请码导入", businessType = BusinessType.IMPORT)
    @ApiOperation(value = "邀请码导入")
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    @PostMapping("/importExcel")
    public AjaxResult<String> importExcel(@RequestPart("file") MultipartFile file) {
        ImportParams params = new ImportParams();
//        params.setTitleRows(1); // 标题行数
        params.setHeadRows(1); //表头行数
        InputStream inputStream = null;
//        List<CustomerImportFailedData> failedData = new ArrayList<>();
        List<InvitationCodeImportExcel> invitationCodeImportExcels;
        try {
            inputStream = file.getInputStream();
            invitationCodeImportExcels = ExcelImportUtil.importExcel(inputStream,
                    InvitationCodeImportExcel.class, params);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("邀请码导入失败:{}", e.getMessage());
            throw new ServiceException("邀请码导入失败!");
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                throw new ServiceException(e.getMessage());
            }
        }
        if (CollectionUtils.isEmpty(invitationCodeImportExcels)) {
            throw new ServiceException("邀请码数据为空!");
        }
        // 校验邀请码是否有重复的
        long count = invitationCodeImportExcels.stream().map(InvitationCodeImportExcel::getInvitationCode)
                .distinct().count();
        if (count!=invitationCodeImportExcels.size()){
            return AjaxResult.error("邀请码存在重复!");
        }
        Map<String, Long> codeMap = xpInvitationCodeService.list().stream()
                .collect(Collectors.toMap(XpInvitationCode::getInvitationCode, XpInvitationCode::getId));
        JSONObject result = new JSONObject();
        List<XpInvitationCode> addList = new ArrayList<>();
        List<XpWhitelist> addWhitelistList = new ArrayList<>();
        for (InvitationCodeImportExcel locationExcel : invitationCodeImportExcels) {
            Long id = codeMap.get(locationExcel.getInvitationCode());
            if (id!=null){
                result.append("邀请码:[", locationExcel.getInvitationCode()+"]已存在");
            }
            locationExcel.setBindTime(locationExcel.getBindTime());
            XpInvitationCode xpInvitationCode = new XpInvitationCode();
            xpInvitationCode.setInvitationCode(locationExcel.getInvitationCode());
            xpInvitationCode.setBindAccount(locationExcel.getBindAccount());
            if (StringUtils.hasLength(locationExcel.getBindTime())){
                String replace = locationExcel.getBindTime().replace(".", "-");
                String res = replace.replace("/", "-");
                locationExcel.setBindTime(res);
            }
            xpInvitationCode.setBindTime(DateUtils.stringToLocalDateTime(locationExcel.getBindTime()));
            if (StringUtils.hasLength(locationExcel.getBindAccount())){
                xpInvitationCode.setStatus(2);
            }else{
                xpInvitationCode.setStatus(1);
            }
            XpWhitelist xpWhitelist = new XpWhitelist();
            xpWhitelist.setAccount(locationExcel.getBindAccount());
            xpWhitelist.setStatus(true);
            xpWhitelist.setIsPriorityDispatch(false);
            xpWhitelist.setWhitelistSource(2);
            addWhitelistList.add(xpWhitelist);
            addList.add(xpInvitationCode);
        }
        if(!result.isEmpty()){
            return AjaxResult.success(result.toString());
        }
        xpInvitationCodeService.saveBatch(addList);
        xpWhitelistService.saveBatch(addWhitelistList);
        return AjaxResult.success();
    }
    @ApiOperation(value = "邀请码导出")
    @PostMapping(value = "/exportExcel")
    public void exportExcel(@RequestBody InvitationPageListQuery query) {
        List<XpInvitationCode> list = xpInvitationCodeService.exportList(query);
        List<XpInvitationCodeExportExcel> xpWhitelistExportExcels = new ArrayList<>();
        for (XpInvitationCode xpInvitationCode : list) {
            XpInvitationCodeExportExcel xpWhitelistExportExcel = new XpInvitationCodeExportExcel();
            xpWhitelistExportExcel.setId(xpInvitationCode.getId());
            xpWhitelistExportExcel.setBind_account(xpInvitationCode.getBindAccount());
            xpWhitelistExportExcel.setInvitationCode(xpInvitationCode.getInvitationCode());
            LocalDateTime createTime = xpInvitationCode.getCreateTime();
            LocalDateTime bindTime = xpInvitationCode.getBindTime();
            // 将LocalDateTime转化为yyyy-MM-dd HH:mm:ss格式字符串
            xpWhitelistExportExcel.setCreateTime(createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            xpWhitelistExportExcel.setBindTime(bindTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            xpWhitelistExportExcels.add(xpWhitelistExportExcel);
        }
        //1.获取excel模板
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpInvitationCodeExportExcel.class, xpWhitelistExportExcels);
        HttpServletResponse response = WebUtils.response();
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        ServletOutputStream outputStream = null;
        try {
            String fileName = URLEncoder.encode("邀请码列表.xls", "utf-8");
            response.setHeader("Content-dispodition", "attachment;filename=" + fileName);
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 批量删除
     */
    @PutMapping("/deleteByIds")
    @ResponseBody
    @ApiOperation(value = "批量删除")
    public AjaxResult deleteByIds(List<Long> ids){
        xpInvitationCodeService.removeBatchByIds(ids);
        return AjaxResult.success();
    }
    private static final String CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final int CODE_LENGTH = 6;
    private static final Random RANDOM = new Random();
    public static String generateInviteCode() {
        StringBuilder code = new StringBuilder(CODE_LENGTH);
        for (int i = 0; i < CODE_LENGTH; i++) {
            int index = RANDOM.nextInt(CHARACTERS.length());
            code.append(CHARACTERS.charAt(index));
        }
        return code.toString();
    }
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/XpWhiteListController.java
New file
@@ -0,0 +1,35 @@
package com.xiaopeng.operation.controller;
import com.xiaopeng.common.core.web.controller.BaseController;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.operation.service.XpInvitationCodeService;
import com.xiaopeng.operation.service.XpSystemSettingService;
import com.xiaopeng.operation.service.XpWhitelistOperlogService;
import com.xiaopeng.operation.service.XpWhitelistService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/api/v1/invitation")
@Api(tags = "邀请码管理")
public class XpWhiteListController extends BaseController {
    private final XpInvitationCodeService xpInvitationCodeService;
    public XpWhiteListController(XpInvitationCodeService xpInvitationCodeService
    ) {
        this.xpInvitationCodeService = xpInvitationCodeService;
    }
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java
@@ -1,9 +1,63 @@
package com.xiaopeng.operation.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.xiaopeng.api.model.account.SysUser;
import com.xiaopeng.api.model.operation.XpInvitationCode;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.api.model.operation.XpWhitelistOperlog;
import com.xiaopeng.common.core.constant.TokenConstants;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.DateUtils;
import com.xiaopeng.common.core.utils.WebUtils;
import com.xiaopeng.common.core.utils.ip.IpUtils;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.common.log.annotation.Log;
import com.xiaopeng.common.log.enums.BusinessType;
import com.xiaopeng.common.security.service.TokenService;
import com.xiaopeng.operation.dto.WhitelistAddDto;
import com.xiaopeng.operation.exportExcle.XpWhitelistExportExcel;
import com.xiaopeng.operation.importExcel.InvitationCodeImportExcel;
import com.xiaopeng.operation.importExcel.WhitelistImportExcel;
import com.xiaopeng.operation.query.InvitationPageListQuery;
import com.xiaopeng.operation.query.WhitelistPageListQuery;
import com.xiaopeng.operation.service.IXpInvitationCodeService;
import com.xiaopeng.operation.service.IXpWhitelistOperlogService;
import com.xiaopeng.operation.service.IXpWhitelistService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * <p>
@@ -13,8 +67,342 @@
 * @author mitao
 * @since 2026-02-03
 */
@Controller
@RequestMapping("/xp-whitelist")
@RequestMapping("/api/v1/whitelist")
@RestController
@Api(tags = "白名单管理")
@Slf4j
public class XpWhitelistController {
    @Resource
    private IXpWhitelistService xpWhitelistService;
    @Resource
    private IXpWhitelistOperlogService xpWhitelistOperlogService;
    @Resource
    private TokenService tokenService;
    @ApiOperation(value = "白名单管理分页列表", response = XpWhitelist.class)
    @PostMapping("/pageList")
    public AjaxResult<PageInfo<XpWhitelist>> pageList(@RequestBody WhitelistPageListQuery query) {
        PageInfo<XpWhitelist> pageList = xpWhitelistService.pageList(query);
        return AjaxResult.ok(pageList);
    }
    /**
     * 新增白名单
     */
    @PostMapping("/add")
    @ResponseBody
    @ApiOperation(value = "新增白名单")
    @Transactional
    public AjaxResult add(@RequestBody WhitelistAddDto dto) {
        if (!StringUtils.hasLength(dto.getAccount())) {
            return AjaxResult.error("请输入账号");
        }
        List<XpWhitelist> addList = new ArrayList<>();
        List<String> accounts = Arrays.asList(dto.getAccount().split(","));
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        StringBuilder operationContent = new StringBuilder("新增账号:");
        StringBuilder accountBuilder = new StringBuilder();
        for (String account : accounts) {
            XpWhitelist xpWhitelist = new XpWhitelist();
            xpWhitelist.setAccount(account);
            xpWhitelist.setStatus(true);
            xpWhitelist.setIsPriorityDispatch(dto.getIsPriorityDispatch());
            xpWhitelist.setWhitelistSource(1);
            addList.add(xpWhitelist);
            accountBuilder.append("\"").append(account).append("\"").append(",");
        }
        XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog();
        xpWhitelistOperlog.setOperationName(sysUser.getNickName());
        xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber());
        xpWhitelistOperlog.setOperationType(1);
        xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr());
        xpWhitelistOperlog.setOperationAccountId(sysUser.getId());
        StringBuilder content = operationContent.append(accountBuilder.substring(0, accountBuilder.length() - 1));
        xpWhitelistOperlog.setOperationContent(content.toString());
        xpWhitelistOperlogService.save(xpWhitelistOperlog);
        xpWhitelistService.saveBatch(addList);
        return AjaxResult.success();
    }
    /**
     * 编辑白名单
     */
    @PostMapping("/edit")
    @ResponseBody
    @ApiOperation(value = "编辑白名单")
    @Transactional
    public AjaxResult edit(@RequestBody XpWhitelist dto) {
        XpWhitelist whitelist = xpWhitelistService.getById(dto.getId());
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        StringBuilder operationContent = new StringBuilder("编辑账号:");
        StringBuilder accountBuilder = new StringBuilder();
        if (!whitelist.getAccount().equals(dto.getAccount())) {
            accountBuilder.append("白名单账号" + "\"" + whitelist.getAccount() + "\"" + "为" + "\"" + dto.getAccount() + "\"");
        }
        if (StringUtils.hasLength(whitelist.getNickName()) || StringUtils.hasLength(dto.getNickName())) {
            if (!StringUtils.hasLength(whitelist.getNickName())) {
                if (!whitelist.getNickName().equals(dto.getNickName())) {
                    accountBuilder.append("用户名称" + "\"" + whitelist.getNickName() + "\"" + "为" + "\"" + dto.getNickName() + "\"");
                }
            } else {
                if (!whitelist.getNickName().equals(dto.getNickName())) {
                    accountBuilder.append("用户名称" + "\"" + "为" + "\"" + dto.getNickName() + "\"");
                }
            }
        }
        if (whitelist.getStatus() != dto.getStatus()) {
            accountBuilder.append("状态" + "\"" + (whitelist.getIsPriorityDispatch() ? "正常" : "冻结") + "\"" + "为" + "\"" + (dto.getIsPriorityDispatch() ? "正常" : "冻结") + "\"");
        }
        if (whitelist.getIsPriorityDispatch() != dto.getIsPriorityDispatch()) {
            accountBuilder.append("优先派单" + "\"" + (whitelist.getIsPriorityDispatch() ? "是" : "否") + "\"" + "为" + "\"" + (dto.getIsPriorityDispatch() ? "是" : "否") + "\"");
        }
        XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog();
        xpWhitelistOperlog.setOperationName(sysUser.getNickName());
        xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber());
        xpWhitelistOperlog.setOperationType(2);
        xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr());
        xpWhitelistOperlog.setOperationAccountId(sysUser.getId());
        accountBuilder.append("\"").append(dto.getAccount()).append("\"");
        StringBuilder content = operationContent.append(accountBuilder);
        xpWhitelistOperlog.setOperationContent(content.toString());
        xpWhitelistOperlogService.save(xpWhitelistOperlog);
        whitelist.setNickName(dto.getNickName());
        whitelist.setIsPriorityDispatch(dto.getIsPriorityDispatch());
        whitelist.setAccount(dto.getAccount());
        whitelist.setStatus(dto.getStatus());
        xpWhitelistService.updateById(whitelist);
        return AjaxResult.success();
    }
    /**
     * 冻结解冻
     */
    @GetMapping("/freezeUnfreeze")
    @ResponseBody
    @ApiOperation(value = "冻结解冻")
    @Transactional
    public AjaxResult freezeUnfreeze(@RequestParam("id") Long id) {
        XpWhitelist whitelist = xpWhitelistService.getById(id);
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        StringBuilder operationContent = new StringBuilder("编辑账号:"+"\""+whitelist.getAccount()+"\"");
        StringBuilder accountBuilder = new StringBuilder();
        accountBuilder.append("状态" + "\"" + (whitelist.getIsPriorityDispatch() ? "正常" : "冻结") + "\"" + "为" + "\"" + (!whitelist.getIsPriorityDispatch() ? "正常" : "冻结") + "\"");
        XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog();
        xpWhitelistOperlog.setOperationName(sysUser.getNickName());
        xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber());
        xpWhitelistOperlog.setOperationType(2);
        xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr());
        xpWhitelistOperlog.setOperationAccountId(sysUser.getId());
        StringBuilder content = operationContent.append(accountBuilder);
        xpWhitelistOperlog.setOperationContent(content.toString());
        xpWhitelistOperlogService.save(xpWhitelistOperlog);
        whitelist.setStatus(!whitelist.getStatus());
        xpWhitelistService.updateById(whitelist);
        return AjaxResult.success();
    }
    /**
     * 批量删除
     */
    @PutMapping("/deleteByIds")
    @ResponseBody
    @ApiOperation(value = "批量删除")
    public AjaxResult deleteByIds(List<Long> ids) {
        List<String> accounts = xpWhitelistService.lambdaQuery().in(XpWhitelist::getId, ids).list()
                .stream().map(XpWhitelist::getAccount).collect(Collectors.toList());
        xpWhitelistService.removeBatchByIds(ids);
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        StringBuilder operationContent = new StringBuilder("删除账号:");
        StringBuilder accountBuilder = new StringBuilder();
        for (String account : accounts) {
            accountBuilder.append("\"" + account + "\""+",");
        }
        XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog();
        xpWhitelistOperlog.setOperationName(sysUser.getNickName());
        xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber());
        xpWhitelistOperlog.setOperationType(3);
        xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr());
        xpWhitelistOperlog.setOperationAccountId(sysUser.getId());
        StringBuilder content = operationContent.append(accountBuilder.substring(0, accountBuilder.length() - 1));
        xpWhitelistOperlog.setOperationContent(content.toString());
        xpWhitelistOperlogService.save(xpWhitelistOperlog);
        return AjaxResult.success();
    }
    /**
     * 批量冻结
     */
    @PutMapping("/freezeByIds")
    @ResponseBody
    @ApiOperation(value = "批量冻结")
    public AjaxResult freezeByIds(List<Long> ids) {
        List<XpWhitelist> list = xpWhitelistService.lambdaQuery().in(XpWhitelist::getId, ids).list();
        for (XpWhitelist xpWhitelist : list) {
            xpWhitelist.setStatus(false);
        }
        xpWhitelistService.updateBatchById(list);
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        StringBuilder operationContent = new StringBuilder("共计冻结"+ids+"条数据");
        XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog();
        xpWhitelistOperlog.setOperationName(sysUser.getNickName());
        xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber());
        xpWhitelistOperlog.setOperationType(6);
        xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr());
        xpWhitelistOperlog.setOperationAccountId(sysUser.getId());
        xpWhitelistOperlog.setOperationContent(operationContent.toString());
        xpWhitelistOperlogService.save(xpWhitelistOperlog);
        return AjaxResult.success();
    }
    @ApiOperation(value = "白名单导入模板下载")
    @GetMapping("/import-template")
    public void importTemplate() {
        List<WhitelistImportExcel> whitelistImportExcels = new ArrayList<>();
        WhitelistImportExcel whitelistImportExcel = new WhitelistImportExcel();
        whitelistImportExcels.add(whitelistImportExcel);
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), WhitelistImportExcel.class, whitelistImportExcels);
        HttpServletResponse response = WebUtils.response();
        ServletOutputStream outputStream = null;
        try {
            String fileName = URLEncoder.encode("白名单导入模板.xls", "utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("白名单导入模板下载失败!");
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @Log(title = "白名单导入", businessType = BusinessType.IMPORT)
    @ApiOperation(value = "白名单导入")
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @PostMapping("/importExcel")
    public AjaxResult<String> importExcel(@RequestPart("file") MultipartFile file) {
        ImportParams params = new ImportParams();
//        params.setTitleRows(1); // 标题行数
        params.setHeadRows(1); //表头行数
        InputStream inputStream = null;
//        List<CustomerImportFailedData> failedData = new ArrayList<>();
        List<WhitelistImportExcel> whitelistImportExcels;
        try {
            inputStream = file.getInputStream();
            whitelistImportExcels = ExcelImportUtil.importExcel(inputStream,
                    WhitelistImportExcel.class, params);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("白名单导入失败:{}", e.getMessage());
            throw new ServiceException("白名单导入失败!");
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                throw new ServiceException(e.getMessage());
            }
        }
        if (CollectionUtils.isEmpty(whitelistImportExcels)) {
            throw new ServiceException("白名单数据为空!");
        }
        // 校验手机号白名单是否有重复的
        long count = whitelistImportExcels.stream().map(WhitelistImportExcel::getAccount)
                .distinct().count();
        if (count != whitelistImportExcels.size()) {
            return AjaxResult.error("白名单手机号存在重复!");
        }
        Map<String, Long> codeMap = xpWhitelistService.list().stream()
                .collect(Collectors.toMap(XpWhitelist::getAccount, XpWhitelist::getId));
        JSONObject result = new JSONObject();
        List<XpWhitelist> addList = new ArrayList<>();
        for (WhitelistImportExcel whitelistImportExcel : whitelistImportExcels) {
            Long id = codeMap.get(whitelistImportExcel.getAccount());
            if (id != null) {
                result.append("白名单:[", whitelistImportExcel.getAccount() + "]已存在");
            }
            XpWhitelist xpWhitelist = new XpWhitelist();
            xpWhitelist.setNickName(whitelistImportExcel.getNickName());
            xpWhitelist.setAccount(whitelistImportExcel.getAccount());
            xpWhitelist.setStatus(true);
            xpWhitelist.setIsPriorityDispatch(true);
            xpWhitelist.setWhitelistSource(1);
            addList.add(xpWhitelist);
        }
        if (!result.isEmpty()) {
            return AjaxResult.success(result.toString());
        }
        xpWhitelistService.saveBatch(addList);
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        StringBuilder operationContent = new StringBuilder("共计导入"+addList.size()+"条数据");
        XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog();
        xpWhitelistOperlog.setOperationName(sysUser.getNickName());
        xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber());
        xpWhitelistOperlog.setOperationType(4);
        xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr());
        xpWhitelistOperlog.setOperationAccountId(sysUser.getId());
        xpWhitelistOperlog.setOperationContent(operationContent.toString());
        xpWhitelistOperlogService.save(xpWhitelistOperlog);
        return AjaxResult.success();
    }
    @ApiOperation(value = "白名单导出")
    @PostMapping(value = "/exportExcel")
    public void exportExcel(@RequestBody WhitelistPageListQuery query) {
        List<XpWhitelist> exportList = xpWhitelistService.exportList(query);
        List<XpWhitelistExportExcel> xpWhitelistExportExcels = new ArrayList<>();
        for (XpWhitelist xpWhitelist : exportList) {
            XpWhitelistExportExcel xpWhitelistExportExcel = new XpWhitelistExportExcel();
            xpWhitelistExportExcel.setId(xpWhitelist.getId());
            xpWhitelistExportExcel.setAccount(xpWhitelist.getAccount());
            xpWhitelistExportExcel.setNickName(xpWhitelist.getNickName());
            xpWhitelistExportExcel.setStatus(xpWhitelist.getStatus());
            xpWhitelistExportExcel.setIsPriorityDispatch(xpWhitelist.getIsPriorityDispatch());
            xpWhitelistExportExcel.setCreateBy(xpWhitelist.getCreateBy());
            LocalDateTime createTime = xpWhitelist.getCreateTime();
            // 将LocalDateTime转化为yyyy-MM-dd HH:mm:ss格式字符串
            xpWhitelistExportExcel.setCreateTime(createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            xpWhitelistExportExcels.add(xpWhitelistExportExcel);
        }
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        StringBuilder operationContent = new StringBuilder("共计导出"+exportList.size()+"条数据");
        XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog();
        xpWhitelistOperlog.setOperationName(sysUser.getNickName());
        xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber());
        xpWhitelistOperlog.setOperationType(5);
        xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr());
        xpWhitelistOperlog.setOperationAccountId(sysUser.getId());
        xpWhitelistOperlog.setOperationContent(operationContent.toString());
        xpWhitelistOperlogService.save(xpWhitelistOperlog);
        //1.获取excel模板
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpWhitelistExportExcel.class, xpWhitelistExportExcels);
        HttpServletResponse response = WebUtils.response();
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        ServletOutputStream outputStream = null;
        try {
            String fileName = URLEncoder.encode("白名单列表.xls", "utf-8");
            response.setHeader("Content-dispodition", "attachment;filename=" + fileName);
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/controller/XpWhitelistOperlogController.java
@@ -1,9 +1,39 @@
package com.xiaopeng.operation.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.api.model.operation.XpWhitelistOperlog;
import com.xiaopeng.common.core.utils.WebUtils;
import com.xiaopeng.common.core.web.domain.AjaxResult;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.exportExcle.XpWhitelistExportExcel;
import com.xiaopeng.operation.exportExcle.XpWhitelistLogExportExcel;
import com.xiaopeng.operation.query.WhitelistLogPageListQuery;
import com.xiaopeng.operation.query.WhitelistPageListQuery;
import com.xiaopeng.operation.service.IXpWhitelistOperlogService;
import com.xiaopeng.operation.service.IXpWhitelistService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
/**
 * <p>
@@ -13,8 +43,58 @@
 * @author mitao
 * @since 2026-02-03
 */
@Controller
@RequestMapping("/xp-whitelist-operlog")
@RequestMapping("/api/v1/whitelistLog")
@RestController
@Api(tags = "白名单管理")
@Slf4j
public class XpWhitelistOperlogController {
    @Resource
    private IXpWhitelistOperlogService xpWhitelistOperlogService;
    @ApiOperation(value = "白名单管理分页列表",response = XpWhitelist.class)
    @PostMapping("/pageList")
    public AjaxResult<PageInfo<XpWhitelistOperlog>> pageList(@RequestBody WhitelistLogPageListQuery query) {
        PageInfo<XpWhitelistOperlog> pageList = xpWhitelistOperlogService.pageList(query);
        return AjaxResult.ok(pageList);
    }
    @ApiOperation(value = "白名单日志导出")
    @PostMapping(value = "/exportExcel")
    public void exportExcel(@RequestBody WhitelistLogPageListQuery query) {
        List<XpWhitelistOperlog> list = xpWhitelistOperlogService.exportList(query);
        List<XpWhitelistLogExportExcel> xpWhitelistLogExportExcels = new ArrayList<>();
        for (XpWhitelistOperlog xpWhitelistOperlog : list) {
            XpWhitelistLogExportExcel xpWhitelistExportExcel = new XpWhitelistLogExportExcel();
            xpWhitelistExportExcel.setId(xpWhitelistOperlog.getId());
            xpWhitelistExportExcel.setOperationAccount(xpWhitelistOperlog.getOperationAccount());
            xpWhitelistExportExcel.setOperationName(xpWhitelistOperlog.getOperationName());
            xpWhitelistExportExcel.setOperationContent(xpWhitelistOperlog.getOperationContent());
            xpWhitelistExportExcel.setOperationIp(xpWhitelistOperlog.getOperationIp());
            xpWhitelistExportExcel.setOperationType(xpWhitelistOperlog.getOperationType());
            LocalDateTime createTime = xpWhitelistOperlog.getCreateTime();
            // 将LocalDateTime转化为yyyy-MM-dd HH:mm:ss格式字符串
            xpWhitelistExportExcel.setCreateTime(createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            xpWhitelistLogExportExcels.add(xpWhitelistExportExcel);
        }
        //1.获取excel模板
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpWhitelistLogExportExcel.class, xpWhitelistLogExportExcels);
        HttpServletResponse response = WebUtils.response();
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        ServletOutputStream outputStream = null;
        try {
            String fileName = URLEncoder.encode("白名单日志列表.xls", "utf-8");
            response.setHeader("Content-dispodition", "attachment;filename=" + fileName);
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/dto/WhitelistAddDto.java
New file
@@ -0,0 +1,18 @@
package com.xiaopeng.operation.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(value = "白名单新增DTO")
public class WhitelistAddDto {
    @ApiModelProperty(value = "账号 多个逗号隔开")
    private String account;
    @ApiModelProperty(value = "优先派单 1=是 0=否")
    private Boolean isPriorityDispatch;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/exportExcle/XpInvitationCodeExportExcel.java
New file
@@ -0,0 +1,34 @@
package com.xiaopeng.operation.exportExcle;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * <p>
 * 邀请码导出
 * </p>
 *
 * @author mitao
 * @since 2026-02-03
 */
@Data
@ApiModel(value="邀请码导出")
public class XpInvitationCodeExportExcel {
    @ApiModelProperty(value = "序号")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "序号",width = 20)
    private Long id;
    @ApiModelProperty(value = "邀请码")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "用户名称",width = 20)
    private String invitationCode;
    @ApiModelProperty(value = "绑定账号")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "绑定账号",width = 20)
    private String bind_account;
    @ApiModelProperty(value = "创建时间,前端忽略")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "创建时间",width = 30)
    private String createTime;
    @ApiModelProperty(value = "创建时间,前端忽略")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "绑定时间",width = 30)
    private String bindTime;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/exportExcle/XpWhitelistExportExcel.java
New file
@@ -0,0 +1,56 @@
package com.xiaopeng.operation.exportExcle;
import com.xiaopeng.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * <p>
 * 白名单导出
 * </p>
 *
 * @author mitao
 * @since 2026-02-03
 */
@Data
@ApiModel(value="白名单导出")
public class XpWhitelistExportExcel {
    @ApiModelProperty(value = "序号")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "序号",width = 20)
    private Long id;
    @ApiModelProperty(value = "用户名称")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "用户名称",width = 20)
    private String nickName;
    @ApiModelProperty(value = "白名单账号")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "白名单账号",width = 20)
    private String account;
    @ApiModelProperty(value = "状态 0=冻结 1=正常")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "状态",width = 20,replace = {"true_正常","false_冻结"})
    private Boolean status;
    @ApiModelProperty(value = "优先派单 1=是 0=否")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "优先派单",width = 20,replace = {"true_是","false_否"})
    private Boolean isPriorityDispatch;
    @ApiModelProperty(value = "记录创建人,前端忽略")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "创建人",width = 20)
    private String createBy;
    @ApiModelProperty(value = "记录创建时间,前端忽略")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "创建时间",width = 30)
    private String createTime;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/exportExcle/XpWhitelistLogExportExcel.java
New file
@@ -0,0 +1,50 @@
package com.xiaopeng.operation.exportExcle;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * <p>
 * 白名单日志导出
 * </p>
 *
 * @author mitao
 * @since 2026-02-03
 */
@Data
@ApiModel(value="白名单日志导出")
public class XpWhitelistLogExportExcel {
    @ApiModelProperty(value = "序号")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "序号",width = 20)
    private Long id;
    @ApiModelProperty(value = "操作人")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "操作人",width = 20)
    private String operationName;
    @ApiModelProperty(value = "操作账号")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "操作账号",width = 20)
    private String operationAccount;
    @ApiModelProperty(value =" 操作类型 1=新增 2=编辑 3=删除 4=导入 5=导出 6=批量冻结")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "操作类型",width = 20,replace = {"1_新增","2_编辑","3_删除","4_导入","5_导出","6_批量冻结"})
    private Integer operationType;
    @ApiModelProperty(value = "操作内容")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "操作内容",width = 20)
    private String operationContent;
    @ApiModelProperty(value = "操作ip")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "操作ip",width = 20)
    private String operationIp;
    @ApiModelProperty(value = "操作时间")
    @cn.afterturn.easypoi.excel.annotation.Excel(name = "操作时间",width = 30)
    private String createTime;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/importExcel/InvitationCodeImportExcel.java
New file
@@ -0,0 +1,18 @@
package com.xiaopeng.operation.importExcel;
import cn.afterturn.easypoi.excel.annotation.Excel;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "邀请码导入excel")
public class InvitationCodeImportExcel implements Serializable {
    @Excel(name = "邀请码",width = 20)
    private String invitationCode;
    @Excel(name = "绑定账号",width = 20)
    private String bindAccount;
    @Excel(name = "绑定时间",width = 30)
    private String bindTime;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/importExcel/WhitelistImportExcel.java
New file
@@ -0,0 +1,16 @@
package com.xiaopeng.operation.importExcel;
import cn.afterturn.easypoi.excel.annotation.Excel;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "白名单导入excel")
public class WhitelistImportExcel implements Serializable {
    @Excel(name = "用户名称",width = 20)
    private String nickName;
    @Excel(name = "白名单账号",width = 20)
    private String account;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/mapper/XpInvitationCodeMapper.java
@@ -2,6 +2,11 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.api.model.operation.XpInvitationCode;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.query.InvitationPageListQuery;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
@@ -13,4 +18,7 @@
 */
public interface XpInvitationCodeMapper extends BaseMapper<XpInvitationCode> {
    List<XpInvitationCode> pageList(@Param("pageInfo") PageInfo<XpInvitationCode> pageInfo, @Param("query") InvitationPageListQuery query);
    List<XpInvitationCode> exportList(@Param("query")InvitationPageListQuery query);
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/mapper/XpWhitelistMapper.java
@@ -2,6 +2,11 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.query.WhitelistPageListQuery;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
@@ -13,4 +18,7 @@
 */
public interface XpWhitelistMapper extends BaseMapper<XpWhitelist> {
    List<XpWhitelist> pageList(@Param("pageInfo") PageInfo<XpWhitelist> pageInfo, @Param("query")WhitelistPageListQuery query);
    List<XpWhitelist> exportList(@Param("query")WhitelistPageListQuery query);
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/mapper/XpWhitelistOperlogMapper.java
@@ -2,6 +2,11 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.api.model.operation.XpWhitelistOperlog;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.query.WhitelistLogPageListQuery;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * <p>
@@ -13,4 +18,7 @@
 */
public interface XpWhitelistOperlogMapper extends BaseMapper<XpWhitelistOperlog> {
    List<XpWhitelistOperlog> pageList(@Param("pageInfo") PageInfo<XpWhitelistOperlog> pageInfo, @Param("query")WhitelistLogPageListQuery query);
    List<XpWhitelistOperlog> exportList(@Param("query")WhitelistLogPageListQuery query);
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/query/InvitationPageListQuery.java
New file
@@ -0,0 +1,18 @@
package com.xiaopeng.operation.query;
import com.xiaopeng.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "邀请码管理列表查询query")
public class InvitationPageListQuery extends BasePage {
    @ApiModelProperty(value = "邀请码")
    private String invitationCode;
    @ApiModelProperty(value = "状态 1=未绑定 2=已绑定 3=已解绑")
    private String status;
    @ApiModelProperty(value = "绑定账号")
    private String bindAccount;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/query/WhitelistLogPageListQuery.java
New file
@@ -0,0 +1,20 @@
package com.xiaopeng.operation.query;
import com.xiaopeng.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "白名单日志列表查询query")
public class WhitelistLogPageListQuery extends BasePage {
    @ApiModelProperty(value = "操作人")
    private String operationName;
    @ApiModelProperty(value = "操作类型 1=新增 2=编辑 3=删除 4=导入 5=导出 6=批量冻结")
    private Integer operationType;
    @ApiModelProperty(value = "开始时间 yyyy-MM-dd HH:mm:ss")
    private String startTime;
    @ApiModelProperty(value = "结束时间 yyyy-MM-dd HH:mm:ss")
    private String endTime;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/query/WhitelistPageListQuery.java
New file
@@ -0,0 +1,18 @@
package com.xiaopeng.operation.query;
import com.xiaopeng.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "白名单管理列表查询query")
public class WhitelistPageListQuery extends BasePage {
    @ApiModelProperty(value = "白名单账号")
    private String account;
    @ApiModelProperty(value = "状态 0=冻结 1=正常")
    private Boolean status;
    @ApiModelProperty(value = "来源 1=添加 2=邀请码")
    private Integer whitelistSource;
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/IXpInvitationCodeService.java
@@ -2,6 +2,11 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.model.operation.XpInvitationCode;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.query.InvitationPageListQuery;
import java.util.List;
/**
 * <p>
@@ -13,4 +18,7 @@
 */
public interface IXpInvitationCodeService extends IService<XpInvitationCode> {
    PageInfo<XpInvitationCode> pageList(InvitationPageListQuery query);
    List<XpInvitationCode> exportList(InvitationPageListQuery query);
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/IXpWhitelistOperlogService.java
@@ -2,6 +2,10 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.model.operation.XpWhitelistOperlog;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.query.WhitelistLogPageListQuery;
import java.util.List;
/**
 * <p>
@@ -13,4 +17,7 @@
 */
public interface IXpWhitelistOperlogService extends IService<XpWhitelistOperlog> {
    PageInfo<XpWhitelistOperlog> pageList(WhitelistLogPageListQuery query);
    List<XpWhitelistOperlog> exportList(WhitelistLogPageListQuery query);
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/IXpWhitelistService.java
@@ -2,6 +2,10 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.query.WhitelistPageListQuery;
import java.util.List;
/**
 * <p>
@@ -13,4 +17,7 @@
 */
public interface IXpWhitelistService extends IService<XpWhitelist> {
    PageInfo<XpWhitelist> pageList(WhitelistPageListQuery query);
    List<XpWhitelist> exportList(WhitelistPageListQuery query);
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/impl/XpInvitationCodeServiceImpl.java
@@ -1,10 +1,16 @@
package com.xiaopeng.operation.service.impl;
import com.xiaopeng.api.model.operation.XpInvitationCode;
import com.xiaopeng.api.model.operation.XpPoint;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.mapper.XpInvitationCodeMapper;
import com.xiaopeng.operation.query.InvitationPageListQuery;
import com.xiaopeng.operation.service.IXpInvitationCodeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
@@ -17,4 +23,18 @@
@Service
public class XpInvitationCodeServiceImpl extends ServiceImpl<XpInvitationCodeMapper, XpInvitationCode> implements IXpInvitationCodeService {
    @Override
    public PageInfo<XpInvitationCode> pageList(InvitationPageListQuery query) {
        PageInfo<XpInvitationCode> pageInfo = new PageInfo<>(query.getPageCurr(),query.getPageSize());
        List<XpInvitationCode> list = this.baseMapper.pageList(pageInfo,query);
        pageInfo.setRecords(list);
        return pageInfo;
    }
    @Override
    public List<XpInvitationCode> exportList(InvitationPageListQuery query) {
        List<XpInvitationCode> list = this.baseMapper.exportList(query);
        return list;
    }
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/impl/XpWhitelistOperlogServiceImpl.java
@@ -1,10 +1,15 @@
package com.xiaopeng.operation.service.impl;
import com.xiaopeng.api.model.operation.XpInvitationCode;
import com.xiaopeng.api.model.operation.XpWhitelistOperlog;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.mapper.XpWhitelistOperlogMapper;
import com.xiaopeng.operation.query.WhitelistLogPageListQuery;
import com.xiaopeng.operation.service.IXpWhitelistOperlogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
@@ -17,4 +22,18 @@
@Service
public class XpWhitelistOperlogServiceImpl extends ServiceImpl<XpWhitelistOperlogMapper, XpWhitelistOperlog> implements IXpWhitelistOperlogService {
    @Override
    public PageInfo<XpWhitelistOperlog> pageList(WhitelistLogPageListQuery query) {
        PageInfo<XpWhitelistOperlog> pageInfo = new PageInfo<>(query.getPageCurr(),query.getPageSize());
        List<XpWhitelistOperlog> list = this.baseMapper.pageList(pageInfo,query);
        pageInfo.setRecords(list);
        return pageInfo;
    }
    @Override
    public List<XpWhitelistOperlog> exportList(WhitelistLogPageListQuery query) {
        List<XpWhitelistOperlog> list = this.baseMapper.exportList(query);
        return list;
    }
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/service/impl/XpWhitelistServiceImpl.java
@@ -1,10 +1,15 @@
package com.xiaopeng.operation.service.impl;
import com.xiaopeng.api.model.operation.XpInvitationCode;
import com.xiaopeng.api.model.operation.XpWhitelist;
import com.xiaopeng.common.core.web.page.PageInfo;
import com.xiaopeng.operation.mapper.XpWhitelistMapper;
import com.xiaopeng.operation.query.WhitelistPageListQuery;
import com.xiaopeng.operation.service.IXpWhitelistService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
@@ -17,4 +22,18 @@
@Service
public class XpWhitelistServiceImpl extends ServiceImpl<XpWhitelistMapper, XpWhitelist> implements IXpWhitelistService {
    @Override
    public PageInfo<XpWhitelist> pageList(WhitelistPageListQuery query) {
        PageInfo<XpWhitelist> pageInfo = new PageInfo<>(query.getPageCurr(),query.getPageSize());
        List<XpWhitelist> list = this.baseMapper.pageList(pageInfo,query);
        pageInfo.setRecords(list);
        return pageInfo;
    }
    @Override
    public List<XpWhitelist> exportList(WhitelistPageListQuery query) {
        List<XpWhitelist> list = this.baseMapper.exportList(query);
        return list;
    }
}
xp-service/xp-operation/src/main/java/com/xiaopeng/operation/vo/IndexDataVO.java
File was deleted
xp-service/xp-operation/src/main/resources/mapper/operation/XpInvitationCodeMapper.xml
@@ -6,5 +6,37 @@
    <sql id="Base_Column_List">
        id, invitation_code, bind_account, bind_time, status, is_deleted, create_time, update_time, create_by, update_by
    </sql>
    <select id="pageList" resultType="com.xiaopeng.api.model.operation.XpInvitationCode">
        select  id,invitation_code as invitationCode,bind_account as bindAccount,bind_time as bindTime,status,is_deleted as deleted,create_time as createTime
        from xp_invitation_code
        where is_deleted = 0
        <if test="query.invitationCode != null and query.invitationCode != ''">
            and invitation_code like concat('%',#{query.invitationCode},'%')
        </if>
        <if test="query.status != null and query.status != ''">
            and status = #{query.status}
        </if>
        <if test="query.bindAccount != null and query.bindAccount != ''">
            and bind_account like concat('%',#{query.bindAccount},'%')
        </if>
        ORDER BY create_time DESC
    </select>
    <select id="exportList" resultType="com.xiaopeng.api.model.operation.XpInvitationCode">
        select  id,invitation_code as invitationCode,bind_account as bindAccount,bind_time as bindTime,status,is_deleted as deleted,create_time as createTime
        from xp_invitation_code
        where is_deleted = 0
        <if test="query.invitationCode != null and query.invitationCode != ''">
            and invitation_code like concat('%',#{query.invitationCode},'%')
        </if>
        <if test="query.status != null and query.status != ''">
            and status = #{query.status}
        </if>
        <if test="query.bindAccount != null and query.bindAccount != ''">
            and bind_account like concat('%',#{query.bindAccount},'%')
        </if>
        ORDER BY create_time DESC
    </select>
</mapper>
xp-service/xp-operation/src/main/resources/mapper/operation/XpWhitelistMapper.xml
@@ -4,7 +4,39 @@
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, nick_name, account, status, is_priority_dispatch, source, is_deleted, create_time, update_time, create_by, update_by
        id, nick_name, account, status, is_priority_dispatch, whitelist_source, is_deleted, create_time, update_time, create_by, update_by
    </sql>
    <select id="pageList" resultType="com.xiaopeng.api.model.operation.XpWhitelist">
        select id,nick_name as nickName,account,status,is_priority_dispatch as isPriorityDispatch,whitelist_source as whitelistSource
        ,create_time as createTime,update_time as updateTime,create_by as createBy,update_by as updateBy
        from xp_whitelist
        where is_deleted = 0
        <if test="query.account != null and query.account != ''">
            and account like concat('%',#{query.account},'%')
        </if>
        <if test="query.status != null">
            and status = #{query.status}
        </if>
        <if test="query.whitelistSource != null">
            and whitelist_source = #{query.source}
        </if>
        order by create_time desc
    </select>
    <select id="exportList" resultType="com.xiaopeng.api.model.operation.XpWhitelist">
        select id,nick_name as nickName,account,status,is_priority_dispatch as isPriorityDispatch,whitelist_source as whitelistSource
        ,create_time as createTime,update_time as updateTime,create_by as createBy,update_by as updateBy
        from xp_whitelist
        where is_deleted = 0
        <if test="query.account != null and query.account != ''">
            and account like concat('%',#{query.account},'%')
        </if>
        <if test="query.status != null">
            and status = #{query.status}
        </if>
        <if test="query.whitelistSource != null">
            and whitelist_source = #{query.source}
        </if>
        order by create_time desc
    </select>
</mapper>
xp-service/xp-operation/src/main/resources/mapper/operation/XpWhitelistOperlogMapper.xml
@@ -6,5 +6,37 @@
    <sql id="Base_Column_List">
        id, operation_name, operation_account, operation_type, operation_content, operation_ip, operation_account_id, is_deleted, create_time, update_time, create_by, update_by
    </sql>
    <select id="pageList" resultType="com.xiaopeng.api.model.operation.XpWhitelistOperlog">
        SELECT
        id,operation_name as operationName, operation_account as operationAccount, operation_type as operationType, operation_content as operationContent, operation_ip as operationIp, operation_account_id as operationAccountId, is_deleted as deleted, create_time as createTime, update_time as updateTime
            FROM xp_whitelist_operlog
        WHERE is_deleted = 0
        <if test="account != null and account != ''">
            AND operation_account LIKE CONCAT('%',#{account},'%')
        </if>
        <if test="operationName != null and operationName != ''">
            AND operation_name LIKE CONCAT('%',#{operationName},'%')
        </if>
        <if test="operationType != null">
            AND operation_type = #{operationType}
        </if>
        order by create_time desc
    </select>
    <select id="exportList" resultType="com.xiaopeng.api.model.operation.XpWhitelistOperlog">
        SELECT
        id,operation_name as operationName, operation_account as operationAccount, operation_type as operationType, operation_content as operationContent, operation_ip as operationIp, operation_account_id as operationAccountId, is_deleted as deleted, create_time as createTime, update_time as updateTime
        FROM xp_whitelist_operlog
        WHERE is_deleted = 0
        <if test="account != null and account != ''">
            AND operation_account LIKE CONCAT('%',#{account},'%')
        </if>
        <if test="operationName != null and operationName != ''">
            AND operation_name LIKE CONCAT('%',#{operationName},'%')
        </if>
        <if test="operationType != null">
            AND operation_type = #{operationType}
        </if>
        order by create_time desc
    </select>
</mapper>
xp-service/xp-order/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -1,9 +1,16 @@
package com.xiaopeng.order.controller;
import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.order.service.IXpOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
/**
 * <p>
@@ -13,8 +20,22 @@
 * @author mitao
 * @since 2026-02-03
 */
@Controller
@RequestMapping("/xp-order")
@RestController
@Api(tags = "订单")
public class XpOrderController {
    @Resource
    private IXpOrderService xpOrderService;
    /**
     * 获取订单统计数据(内部接口)
     *
     * @param query 查询参数
     * @return 订单统计数据
     */
    @ApiOperation(value = "获取订单统计数据", tags = {"内部接口"})
    @PostMapping("/inner/order/statistics")
    public R<OrderStatisticsDTO> getOrderStatistics(@RequestBody StatisticsQueryDTO query) {
        return R.ok(xpOrderService.getOrderStatistics(query));
    }
}
xp-service/xp-order/src/main/java/com/xiaopeng/order/mapper/XpOrderMapper.java
@@ -1,7 +1,13 @@
package com.xiaopeng.order.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaopeng.api.dto.statistics.DailyCountDTO;
import com.xiaopeng.api.model.order.XpOrder;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
/**
 * <p>
@@ -13,4 +19,35 @@
 */
public interface XpOrderMapper extends BaseMapper<XpOrder> {
    /**
     * 统计已完成订单总数(状态9=待评价或10=已完成)
     *
     * @return 订单总数
     */
    Long countCompletedOrders();
    /**
     * 统计时间范围内新增订单数(状态9或10)
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 新增订单数
     */
    Long countNewOrders(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
    /**
     * 统计总交易金额(pay_money累加,状态9或10)
     *
     * @return 总交易金额
     */
    BigDecimal sumTotalMoney();
    /**
     * 获取每日新增订单数(状态9或10)
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 每日统计列表
     */
    List<DailyCountDTO> getDailyOrderCount(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate);
}
xp-service/xp-order/src/main/java/com/xiaopeng/order/service/IXpOrderService.java
@@ -1,6 +1,8 @@
package com.xiaopeng.order.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.model.order.XpOrder;
/**
@@ -13,4 +15,11 @@
 */
public interface IXpOrderService extends IService<XpOrder> {
    /**
     * 获取订单统计数据
     *
     * @param query 查询参数
     * @return 订单统计数据
     */
    OrderStatisticsDTO getOrderStatistics(StatisticsQueryDTO query);
}
xp-service/xp-order/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -1,10 +1,18 @@
package com.xiaopeng.order.service.impl;
import com.xiaopeng.api.dto.statistics.DailyCountDTO;
import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO;
import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO;
import com.xiaopeng.api.model.order.XpOrder;
import com.xiaopeng.order.mapper.XpOrderMapper;
import com.xiaopeng.order.service.IXpOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
/**
 * <p>
@@ -17,4 +25,32 @@
@Service
public class XpOrderServiceImpl extends ServiceImpl<XpOrderMapper, XpOrder> implements IXpOrderService {
    @Override
    public OrderStatisticsDTO getOrderStatistics(StatisticsQueryDTO query) {
        OrderStatisticsDTO dto = new OrderStatisticsDTO();
        // 1. 查询订单总数(状态为9=待评价或10=已完成)
        dto.setTotalCount(baseMapper.countCompletedOrders());
        // 2. 查询时间范围内新增订单数
        if (query.getStartDate() != null && query.getEndDate() != null) {
            dto.setNewCount(baseMapper.countNewOrders(query.getStartDate(), query.getEndDate()));
        } else {
            dto.setNewCount(0L);
        }
        // 3. 查询总交易金额和总收入(pay_money累加)
        BigDecimal totalMoney = baseMapper.sumTotalMoney();
        dto.setTotalMoney(totalMoney != null ? totalMoney : BigDecimal.ZERO);
        dto.setTotalIncome(totalMoney != null ? totalMoney : BigDecimal.ZERO);
        // 4. 查询近N天每日新增订单数(默认30天)
        Integer trendDays = query.getTrendDays() != null ? query.getTrendDays() : 30;
        LocalDate endDate = LocalDate.now();
        LocalDate startDate = endDate.minusDays(trendDays - 1);
        List<DailyCountDTO> dailyCountList = baseMapper.getDailyOrderCount(startDate, endDate);
        dto.setDailyCountList(dailyCountList != null ? dailyCountList : new ArrayList<>());
        return dto;
    }
}
xp-service/xp-order/src/main/resources/mapper/order/XpOrderMapper.xml
@@ -7,4 +7,42 @@
        id, app_user_id, vehicle_id, fleet_id, order_number, transaction_id, order_source, ride_time, placement_lon, placement_lat, placement_address, start_lon, start_lat, end_lon, end_lat, start_address, end_address, boarding_lon, boarding_lat, boarding_address, boarding_time, getoff_lon, getoff_lat, getoff_address, getoff_time, pay_type, order_money, pay_money, prepay_money, start_mileage, start_money, mileage_kilometers, mileage_money, duration, duration_money, wait, wait_money, long_distance, long_distanceMoney, red_packet_money, coupon_money, red_packet_id, coupon_id, status, is_deleted, create_time, update_time, create_by, update_by
    </sql>
    <!-- 统计已完成订单总数(状态9=待评价或10=已完成) -->
    <select id="countCompletedOrders" resultType="java.lang.Long">
        SELECT COUNT(*)
        FROM xp_order
        WHERE is_deleted = 0
          AND status IN (9, 10)
    </select>
    <!-- 统计时间范围内新增订单数 -->
    <select id="countNewOrders" resultType="java.lang.Long">
        SELECT COUNT(*)
        FROM xp_order
        WHERE is_deleted = 0
          AND status IN (9, 10)
          AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
    </select>
    <!-- 统计总交易金额(pay_money累加,状态9或10) -->
    <select id="sumTotalMoney" resultType="java.math.BigDecimal">
        SELECT COALESCE(SUM(pay_money), 0)
        FROM xp_order
        WHERE is_deleted = 0
          AND status IN (9, 10)
    </select>
    <!-- 获取每日新增订单数 -->
    <select id="getDailyOrderCount" resultType="com.xiaopeng.api.dto.statistics.DailyCountDTO">
        SELECT
            DATE_FORMAT(create_time, '%Y-%m-%d') as date,
            COUNT(*) as count
        FROM xp_order
        WHERE is_deleted = 0
          AND status IN (9, 10)
          AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
        GROUP BY DATE(create_time)
        ORDER BY date ASC
    </select>
</mapper>