“mitao”
2026-02-04 31d247f28e83a2133c3e02fa6f16d2ce282bf8f5
feat(management):帐号管理、角色管理
4个文件已删除
2 文件已重命名
11个文件已添加
34个文件已修改
2249 ■■■■■ 已修改文件
xp-api/src/main/java/com/xiaopeng/api/factory/operation/RemoteMenuFallbackFactory.java 12 ●●●●● 补丁 | 查看 | 原始文档 | 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 5 ●●●●● 补丁 | 查看 | 原始文档 | 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 2 ●●●●● 补丁 | 查看 | 原始文档 | 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/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/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/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/vo/IndexDataVO.java 77 ●●●●● 补丁 | 查看 | 原始文档 | 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/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,17 +12,17 @@
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, String source) {
            public R<List<SysMenu>> getMenusByMenuIds(List<Long> menuIds) {
                log.error("获取菜单列表失败,menuIds: {}, 原因: {}", menuIds, cause.getMessage());
                // 降级返回空列表,避免登录失败
                return R.ok(new ArrayList<>());
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树结构实体类
@@ -38,7 +39,7 @@
    public TreeSelect(SysMenuBak menu)
    {
        this.id = menu.getId();
        this.id = menu.getMenuId();
        this.label = menu.getMenuName();
        this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
    }
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
@@ -40,7 +40,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/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/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/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/vo/IndexDataVO.java
File was deleted
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>