feat(app): 重构APP模块支持TOB 添加角色底部导航管理功能,更新相关接口和实体

This commit is contained in:
PIGCLOUD committed 2026-05-10 10:57:59 +08:00
1 parent c48d5f753c
commit a1edbfcc6e
72 files changed
+1118 -3184

No files matched your search

@@ -0,0 +1,38 @@
package com.pig4cloud.pigx.app.api.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* App 角色底部导航配置请求体。
* <p>
* 后台保存某个系统角色可见的 App Tabbar 列表。公开 Tabbar 由 {@code app_tabbar.login_flag=0}
* 控制,不依赖这里的角色授权。
*
* @author lengleng
* @date 2026-05-09
*/
@Data
@Schema(description = "App角色底部导航配置")
public class AppRoleTabbarDTO implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull(message = "角色ID不能为空")
@Schema(description = "系统角色ID")
private Long roleId;
/**
* 该角色可见的登录态 Tabbar ID 列表。
* <p>
* 保存时采用覆盖语义,空列表表示清空该角色的 Tabbar 授权。
*/
@Schema(description = "底部导航ID列表")
private List<Long> tabbarIds = new ArrayList<>();
}
@@ -1,30 +0,0 @@
package com.pig4cloud.pigx.app.api.dto;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@Data
@Schema(description = "APP用户传输对象")
@EqualsAndHashCode(callSuper = true)
public class AppUserDTO extends AppUser {
/**
* 角色ID
*/
@Schema(description = "角色id集合")
private List<Long> role;
/**
* 新密码
*/
@Schema(description = "新密码")
private String newpassword1;
@Schema(description = "验证码")
private String mobileCode;
}
@@ -1,54 +0,0 @@
/*
*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*
*/
package com.pig4cloud.pigx.app.api.dto;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* @author lengleng
* @date 2017/11/11
*/
@Data
@Schema(description = "用户信息")
public class AppUserInfo implements Serializable {
/**
* 用户基本信息
*/
@Schema(description = "用户基本信息")
private AppUser appUser;
/**
* 权限标识集合
*/
@Schema(description = "权限标识集合")
private String[] permissions;
/**
* 角色集合
*/
@Schema(description = "角色标识集合")
private Long[] roles;
}
@@ -22,66 +22,66 @@ import java.time.LocalDateTime;
@Schema(description = "文章收藏表")
public class AppArticleCollectEntity extends Model<AppArticleCollectEntity> {
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private Long id;
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private Long id;
/**
* 用户ID
*/
@Schema(description = "用户ID")
private Long userId;
/**
* 用户ID
*/
@Schema(description = "用户ID")
private Long userId;
/**
* 文章ID
*/
@Schema(description = "文章ID")
private Long articleId;
/**
* 文章ID
*/
@Schema(description = "文章ID")
private Long articleId;
/**
* 文章标题
*/
@TableField(exist = false)
private String title;
/**
* 文章标题
*/
@TableField(exist = false)
private String title;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 更新人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新人")
private String updateBy;
/**
* 更新人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新人")
private String updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新时间")
private LocalDateTime updateTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新时间")
private LocalDateTime updateTime;
/**
* 是否删除
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "是否删除")
private String delFlag;
/**
* 是否删除
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "是否删除")
private String delFlag;
/**
* 图片
@@ -0,0 +1,92 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 通讯录表
*
* @author pigx
* @date 2025-05-29 17:30:30
*/
@Data
@TenantTable
@TableName("app_contacts")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "通讯录表")
public class AppContactsEntity extends Model<AppContactsEntity> {
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private Long id;
/**
* 联系人
*/
@Schema(description = "联系人")
private String contactName;
/**
* 手机号
*/
@Schema(description = "手机号")
private String contactPhone;
/**
* 备注
*/
@Schema(description = "备注")
private String remark;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 修改人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "修改人")
private String updateBy;
/**
* 修改时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "修改时间")
private LocalDateTime updateTime;
/**
* 删除标记
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "删除标记")
private String delFlag;
/**
* 租户ID
*/
@Schema(description = "租户ID")
private Long tenantId;
}
@@ -6,11 +6,15 @@ import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.FieldNameConstants;
import java.time.LocalDateTime;
/**
* 页面
* App 装修页面实体。
* <p>
* 仅对应 {@code app_page} 表,保存页面类型、页面名称和页面组件 JSON。
* 底部 Tabbar 已拆分到 {@code app_tabbar},不要再塞进 {@link #pageData}。
*
* @author lengleng
* @date 2023-06-08 11:19:23
@@ -18,6 +22,7 @@ import java.time.LocalDateTime;
@Data
@TenantTable
@TableName("app_page")
@FieldNameConstants
@EqualsAndHashCode(callSuper = true)
@Schema(description = "页面")
public class AppPageEntity extends Model<AppPageEntity> {
@@ -30,7 +35,7 @@ public class AppPageEntity extends Model<AppPageEntity> {
private Long id;
/**
* 页面类型
* 页面类型,对应 {@link com.pig4cloud.pigx.app.api.enums.PageTypeEnums}
*/
@Schema(description = "页面类型")
private Integer pageType;
@@ -42,7 +47,10 @@ public class AppPageEntity extends Model<AppPageEntity> {
private String pageName;
/**
* 页面数据
* 页面装修组件 JSON。
* <p>
* 只保存页面内部组件,例如搜索、轮播、导航块、服务块等;
* 运行时需要 Tabbar 时由服务层聚合 {@code app_tabbar} 返回。
*/
@Schema(description = "页面数据")
private String pageData;
@@ -1,107 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* app角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@Data
@TenantTable
@TableName("app_role")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "app角色表")
public class AppRole extends Model<AppRole> {
private static final long serialVersionUID = 1L;
/**
* roleId
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "roleId")
private Long roleId;
/**
* roleName
*/
@Schema(description = "roleName")
private String roleName;
/**
* roleCode
*/
@Schema(description = "roleCode")
private String roleCode;
/**
* roleDesc
*/
@Schema(description = "roleDesc")
private String roleDesc;
/**
* 创建人
*/
@Schema(description = "创建人")
private String createBy;
/**
* 修改人
*/
@Schema(description = "修改人")
private String updateBy;
/**
* createTime
*/
@Schema(description = "createTime")
private LocalDateTime createTime;
/**
* updateTime
*/
@Schema(description = "updateTime")
private LocalDateTime updateTime;
/**
* delFlag
*/
@Schema(description = "delFlag")
private String delFlag;
/**
* tenantId
*/
@Schema(description = "tenantId", hidden = true)
private Long tenantId;
}
@@ -0,0 +1,37 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* App 角色底部导航关联实体,以 (role_id, tabbar_id, tenant_id) 为复合主键。
* <p>
* 这里的 roleId 是系统角色 {@code sys_role.role_id}。运行时不会跨库查询 {@code sys_user_role},
* 而是从登录 token 的角色权限中解析 roleId,再回到本表查询可见 Tabbar。
*
* @author lengleng
* @date 2026-05-09
*/
@Data
@TenantTable
@TableName("app_role_tabbar")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "App角色底部导航")
public class AppRoleTabbar extends Model<AppRoleTabbar> {
private static final long serialVersionUID = 1L;
@Schema(description = "系统角色ID")
private Long roleId;
@Schema(description = "底部导航ID")
private Long tabbarId;
@Schema(description = "租户ID", hidden = true)
private Long tenantId;
}
@@ -1,125 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.ValidGroup;
import com.pig4cloud.pigx.common.sensitive.annotation.Sensitive;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* app 社交账号登录
*/
@Data
@Schema(description = "第三方账号信息")
@EqualsAndHashCode(callSuper = true)
public class AppSocialDetails extends Model<AppSocialDetails> {
private static final long serialVersionUID = 1L;
/**
* 主鍵
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private Long id;
/**
* 类型
*/
@NotBlank(message = "类型不能为空")
@Schema(description = "账号类型")
private String type;
/**
* 描述
*/
@Schema(description = "描述")
private String remark;
/**
* appid
*/
@Sensitive(prefixNoMaskLen = 4, suffixNoMaskLen = 4)
@NotBlank(message = "账号不能为空")
@Schema(description = "appId")
private String appId;
/**
* app_secret
*/
@Sensitive(prefixNoMaskLen = 4, suffixNoMaskLen = 4)
@NotBlank(message = "密钥不能为空", groups = { ValidGroup.Insert.class })
@Schema(description = "app secret")
private String appSecret;
/**
* 回调地址
*/
@Schema(description = "回调地址")
private String redirectUrl;
/**
* 拓展字段
*/
@Schema(description = "拓展字段")
private String ext;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 修改人
*/
@TableField(fill = FieldFill.UPDATE)
@Schema(description = "修改人")
private String updateBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@Schema(description = "更新时间")
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 删除标记
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "删除标记,1:已删除,0:正常")
private String delFlag;
}
@@ -12,7 +12,10 @@ import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 导航栏
* App 底部导航实体。
* <p>
* 该表是移动端底部 Tabbar 的唯一数据源。图标字段保存 Wot UI 图标名,
* 运行时可见性由 {@link #loginFlag} 和 {@code app_role_tabbar} 共同决定。
*
* @author lengleng
* @date 2023-06-08 11:18:46
@@ -38,19 +41,25 @@ public class AppTabbarEntity extends Model<AppTabbarEntity> {
private String name;
/**
* 未选图标
* 选中态图标名。
* <p>
* 保存 Wot UI 内置图标名称,例如 {@code home-fill}。
*/
@Schema(description = "未选图标")
@Schema(description = "选中态图标名")
private String selected;
/**
* 已选图标
* 未选中态图标名。
* <p>
* 保存 Wot UI 内置图标名称,例如 {@code home}。
*/
@Schema(description = "已选图标")
@Schema(description = "未选中态图标名")
private String unselected;
/**
* 链接地址
* 跳转链接 JSON。
* <p>
* 前端按装修链接结构解析,例如 {@code {"path":"/pages/index/index","type":"shop"}}。
*/
@Schema(description = "链接地址")
@JsonRawValue
@@ -63,6 +72,12 @@ public class AppTabbarEntity extends Model<AppTabbarEntity> {
@Schema(description = "排序值")
private Integer sortOrder;
/**
* 是否需要登录,0=不需要登录也可见,1=登录后按角色授权可见。
*/
@Schema(description = "是否需要登录,0=不需要登录也可见,1=登录后按角色授权可见")
private String loginFlag;
/**
* 创建人
*/
@@ -1,161 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* app用户表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@Data
@TenantTable
@TableName("app_user")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "app用户表")
public class AppUser extends Model<AppUser> {
private static final long serialVersionUID = 1L;
/**
* userId
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "userId")
private Long userId;
/**
* 用户名
*/
@Schema(description = "用户名")
private String username;
/**
* 密码
*/
@Schema(description = "密码")
private String password;
/**
* 随机盐
*/
@JsonIgnore
@Schema(description = "随机盐")
private String salt;
/**
* 手机号
*/
@Schema(description = "手机号")
private String phone;
/**
* 头像
*/
@Schema(description = "头像")
private String avatar;
/**
* 昵称
*/
@Schema(description = "拓展字段:昵称")
private String nickname;
/**
* 姓名
*/
@Schema(description = "拓展字段:姓名")
private String name;
/**
* 邮箱
*/
@Schema(description = "拓展字段:邮箱")
private String email;
/**
* 创建人
*/
@Schema(description = "创建人")
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
* 修改人
*/
@Schema(description = "修改人")
@TableField(fill = FieldFill.UPDATE)
private String updateBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改时间
*/
@Schema(description = "修改时间")
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
/**
* 删除标记
*/
@Schema(description = "删除标记,1:已删除,0:正常")
@TableField(fill = FieldFill.INSERT)
@TableLogic
private String delFlag;
/**
* 所属租户
*/
@Schema(description = "所属租户", hidden = true)
private Long tenantId;
/**
* 最后一次密码修改时间
*/
@Schema(description = "最后一次密码修改时间")
private LocalDateTime lastModifiedTime;
/**
* 锁定标记
*/
@Schema(description = "锁定标记")
private String lockFlag;
/**
* 微信小程序登录openId
*/
@Schema(description = "微信小程序登录openId")
private String wxOpenid;
}
@@ -1,55 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@Data
@TableName("app_user_role")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "用户角色表")
public class AppUserRole extends Model<AppUserRole> {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "用户ID")
private Long userId;
/**
* 角色ID
*/
@Schema(description = "角色ID")
private Long roleId;
}
@@ -1,11 +1,18 @@
package com.pig4cloud.pigx.app.api.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 页面类型枚举
* App 装修页面类型枚举。
* <p>
* 页面类型会写入 {@code app_page.page_type},后台保存装修页时根据该枚举回填页面名称。
*
* @author lengleng
* @date 2024/12/22
*/
@Getter
@RequiredArgsConstructor
public enum PageTypeEnums {
/**
* 首页
@@ -18,17 +25,23 @@ public enum PageTypeEnums {
/**
* 客服设置
*/
SERVICE(3, "客服设置");
SERVICE(3, "客服设置"),
/**
* 工作台属性
*/
WORKBENCH(4, "工作台");
/**
* 页面类型编码,和 app_page.page_type 对应。
*/
private final Integer pageType;
/**
* 页面类型展示名称。
*/
private final String name;
PageTypeEnums(Integer pageType, String name) {
this.pageType = pageType;
this.name = name;
}
/**
* 按类型获取名称
*
@@ -44,11 +57,4 @@ public enum PageTypeEnums {
return null;
}
public Integer getPageType() {
return pageType;
}
public String getName() {
return name;
}
}
@@ -1,58 +0,0 @@
/*
*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*
*/
package com.pig4cloud.pigx.app.api.feign;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author lengleng
* @date 2018/6/22
*/
@FeignClient(contextId = "remoteAppUserService", value = ServiceNameConstants.APP_SERVER)
public interface RemoteAppUserService {
/**
* 通过用户名查询用户、角色信息
* @param username 用户名
* @param from 调用标志
* @return R
*/
@NoToken
@GetMapping("/appuser/info/{username}")
R<AppUserInfo> info(@PathVariable("username") String username);
/**
* 通过社交账号或手机号查询用户、角色信息
* @param inStr appid@code
* @param from 调用标志
* @return
*/
@NoToken
@GetMapping("/appsocial/info/{inStr}")
R<AppUserInfo> social(@PathVariable("inStr") String inStr);
}
@@ -0,0 +1,102 @@
package com.pig4cloud.pigx.app.api.vo;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import java.time.LocalDateTime;
import java.util.List;
/**
* APP 装修页聚合数据。
* <p>
* 对外保持页面字段在顶层,额外追加 {@link #tabbar},避免把底部导航配置重新写回
* {@code app_page.page_data}。
*
* @author lengleng
*/
@Data
@Schema(description = "APP 装修页聚合数据")
public class AppDecorateVO {
/**
* 主键
*/
@Schema(description = "主键")
private Long id;
/**
* 页面类型
*/
@Schema(description = "页面类型")
private Integer pageType;
/**
* 页面名称
*/
@Schema(description = "页面名称")
private String pageName;
/**
* 页面装修组件 JSON,不包含底部 Tabbar。
*/
@Schema(description = "页面数据")
private String pageData;
/**
* 创建人
*/
@Schema(description = "创建人")
private String createBy;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 修改人
*/
@Schema(description = "修改人")
private String updateBy;
/**
* 修改时间
*/
@Schema(description = "修改时间")
private LocalDateTime updateTime;
/**
* 删除标记
*/
@Schema(description = "删除标记")
private String delFlag;
/**
* 当前查询场景下可用的底部导航栏。
* <p>
* 后台装修查询返回完整 Tabbar;App 运行时查询返回公开 Tabbar + 当前角色授权 Tabbar。
*/
@Schema(description = "底部导航栏")
private List<AppTabbarEntity> tabbar;
/**
* 根据页面实体和 Tabbar 列表构建装修聚合 VO。
*
* @param page 页面实体,可能为空
* @param tabbar Tabbar 列表
* @return 装修聚合数据
*/
public static AppDecorateVO of(AppPageEntity page, List<AppTabbarEntity> tabbar) {
AppDecorateVO vo = new AppDecorateVO();
if (page != null) {
BeanUtils.copyProperties(page, vo);
}
vo.setTabbar(tabbar);
return vo;
}
}
@@ -1,61 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.api.vo;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author lengleng
* @date 2020/2/10
*/
@Data
@Schema(description = "前端角色展示对象")
@ColumnWidth(30)
public class AppRoleExcelVO {
/**
* 导入时候回显行号
*/
@ExcelLine
@ExcelIgnore
private Long lineNum;
/**
* roleName
*/
@ExcelProperty("角色名称")
private String roleName;
/**
* roleCode
*/
@ExcelProperty("角色标识")
private String roleCode;
/**
* roleDesc
*/
@ExcelProperty("角色描述")
private String roleDesc;
}
@@ -1,41 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.api.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author lengleng
* @date 2020/2/10
*/
@Data
@Schema(description = "前端角色展示对象")
public class AppRoleVO {
/**
* 角色id
*/
private Long roleId;
/**
* 菜单列表
*/
private String menuIds;
}
@@ -1,83 +0,0 @@
package com.pig4cloud.pigx.app.api.vo;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import com.pig4cloud.pigx.common.excel.annotation.ExcelLine;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@ColumnWidth(30)
public class AppUserExcelVO {
/**
* 导入时候回显行号
*/
@ExcelLine
@ExcelIgnore
private Long lineNum;
/**
* 主键ID
*/
@ExcelProperty("用户编号")
private Long userId;
/**
* 用户名
*/
@NotBlank(message = "用户名不能为空")
@ExcelProperty("用户名")
private String username;
/**
* 手机号
*/
@NotBlank(message = "手机号不能为空")
@ExcelProperty("手机号")
private String phone;
/**
* 手机号
*/
@NotBlank(message = "昵称不能为空")
@ExcelProperty("昵称")
private String nickname;
/**
* 手机号
*/
@NotBlank(message = "姓名不能为空")
@ExcelProperty("姓名")
private String name;
/**
* 手机号
*/
@NotBlank(message = "邮箱不能为空")
@ExcelProperty("邮箱")
private String email;
/**
* 角色列表
*/
@NotBlank(message = "角色不能为空")
@ExcelProperty("角色")
private String roleNameList;
/**
* 锁定标记
*/
@ExcelProperty("锁定标记,0:正常,9:已锁定")
private String lockFlag;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private LocalDateTime createTime;
}
@@ -1,101 +0,0 @@
package com.pig4cloud.pigx.app.api.vo;
import com.pig4cloud.pigx.app.api.entity.AppRole;
import com.pig4cloud.pigx.common.sensitive.annotation.Sensitive;
import com.pig4cloud.pigx.common.sensitive.core.SensitiveTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class AppUserVO implements Serializable {
/**
* 主键
*/
@Schema(description = "主键")
private Long userId;
/**
* 用户名
*/
@Schema(description = "用户名")
private String username;
/**
* 密码
*/
@Schema(description = "密码")
private String password;
/**
* 手机号
*/
@Schema(description = "手机号")
@Sensitive(type = SensitiveTypeEnum.MOBILE_PHONE)
private String phone;
/**
* 头像
*/
@Schema(description = "头像")
private String avatar;
/**
* 昵称
*/
@Schema(description = "拓展字段:昵称")
private String nickname;
/**
* 姓名
*/
@Schema(description = "拓展字段:姓名")
private String name;
/**
* 邮箱
*/
@Schema(description = "拓展字段:邮箱")
private String email;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 删除标记
*/
@Schema(description = "删除标记,1:已删除,0:正常")
private String delFlag;
/**
* 所属租户
*/
@Schema(description = "所属租户")
private Long tenantId;
/**
* 最后一次密码修改时间
*/
@Schema(description = "最后一次密码修改时间")
private LocalDateTime lastModifiedTime;
/**
* 锁定标记
*/
@Schema(description = "锁定标记")
private String lockFlag;
/**
* 角色列表
*/
@Schema(description = "拥有的角色列表")
private List<AppRole> roleList;
}
@@ -1 +0,0 @@
com.pig4cloud.pigx.app.api.feign.RemoteAppUserService
@@ -36,10 +36,13 @@ public class AppArticleController {
private final AppArticleService appArticleService;
/**
* 分页查询
* 分页查询文章资讯。
* <p>
* 返回文章列表时会关联分类名称,供 App 资讯列表和后台管理复用。
*
* @param page 分页对象
* @param appArticle 文章资讯
* @return
* @return 文章分页结果
*/
@Operation(summary = "分页查询", description = "分页查询")
@Inner(value = false)
@@ -49,9 +52,13 @@ public class AppArticleController {
}
/**
* 通过id查询文章资讯
* @param id id
* @return R
* 查询文章详情。
* <p>
* 会递增访问次数;如果传入 userId,则额外返回该用户是否收藏当前文章。
*
* @param id 文章 ID
* @param userId 用户 ID,可为空
* @return 文章详情
*/
@Inner(value = false)
@Operation(summary = "通过id查询", description = "通过id查询")
@@ -0,0 +1,122 @@
package com.pig4cloud.pigx.app.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.app.api.entity.AppContactsEntity;
import com.pig4cloud.pigx.app.service.AppContactsService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.http.HttpHeaders;
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 通讯录表管理控制器
*
* @author lengleng
* @date 2025/05/29
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/appContacts")
@Tag(description = "appContacts", name = "通讯录表管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class AppContactsController {
private final AppContactsService appContactsService;
/**
* 分页查询
*
* @param page 分页对象
* @param appContacts 通讯录表
* @return
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@HasPermission("app_appContacts_view")
public R getAppContactsPage(@ParameterObject Page page, @ParameterObject AppContactsEntity appContacts) {
return R.ok(appContactsService.pageScope(page, appContacts));
}
/**
* 通过id查询通讯录表
*
* @param id id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}")
@HasPermission("app_appContacts_view")
public R getById(@PathVariable("id") Long id) {
return R.ok(appContactsService.getById(id));
}
/**
* 新增通讯录表
*
* @param appContacts 通讯录表
* @return R
*/
@Operation(summary = "新增通讯录表", description = "新增通讯录表")
@SysLog("新增通讯录表")
@PostMapping
@HasPermission("app_appContacts_add")
public R save(@RequestBody AppContactsEntity appContacts) {
return R.ok(appContactsService.save(appContacts));
}
/**
* 修改通讯录表
*
* @param appContacts 通讯录表
* @return R
*/
@Operation(summary = "修改通讯录表", description = "修改通讯录表")
@SysLog("修改通讯录表")
@PutMapping
@HasPermission("app_appContacts_edit")
public R updateById(@RequestBody AppContactsEntity appContacts) {
return R.ok(appContactsService.updateById(appContacts));
}
/**
* 通过id删除通讯录表
*
* @param ids id列表
* @return R
*/
@Operation(summary = "通过id删除通讯录表", description = "通过id删除通讯录表")
@SysLog("通过id删除通讯录表")
@DeleteMapping
@HasPermission("app_appContacts_del")
public R removeById(@RequestBody Long[] ids) {
return R.ok(appContactsService.removeBatchByIds(CollUtil.toList(ids)));
}
/**
* 导出excel 表格
*
* @param appContacts 查询条件
* @param ids 导出指定ID
* @return excel 文件流
*/
@ResponseExcel
@GetMapping("/export")
@HasPermission("app_appContacts_export")
public List<AppContactsEntity> export(AppContactsEntity appContacts, Long[] ids) {
return appContactsService.list(Wrappers.lambdaQuery(appContacts).in(ArrayUtil.isNotEmpty(ids), AppContactsEntity::getId, ids));
}
}
@@ -1,6 +1,5 @@
package com.pig4cloud.pigx.app.controller;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
import com.pig4cloud.pigx.app.service.AppIndexService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.security.annotation.Inner;
@@ -14,10 +13,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* app 首页控制
* App 运行时页面接口。
* <p>
* 这里的接口主要给移动端调用,返回首页、装修页、底部 Tabbar 等运行时数据。
* 部分接口允许匿名访问,具体可见方法上的 {@link Inner} 配置。
*
* @author lengleng
* @date 2023/6/8
@@ -32,9 +32,11 @@ public class AppIndexController {
private final AppIndexService indexService;
/**
* 获取主页配置
* 获取首页基础数据。
* <p>
* 当前返回首页装修页记录和资讯列表,适合旧版首页一次性拉取基础内容。
*
* @return {@link R }
* @return 首页页面数据和资讯列表
*/
@Inner(value = false)
@GetMapping("/index")
@@ -43,9 +45,11 @@ public class AppIndexController {
}
/**
* 获取移动端appTabbar 配置
* 获取移动端底部导航配置。
* <p>
* 未登录时只返回公开 Tabbar;登录后返回公开 Tabbar + 当前用户角色授权的 Tabbar。
*
* @return {@link R }
* @return 当前访问身份可见的 Tabbar 列表
*/
@Inner(value = false)
@GetMapping("/config")
@@ -54,10 +58,12 @@ public class AppIndexController {
}
/**
* 根据页面类型查询页面配置
* 获取指定页面类型的装修数据。
* <p>
* 返回页面装修 JSON,同时聚合当前访问身份可见的 Tabbar,方便移动端一次请求完成页面渲染。
*
* @param pageType 页面类型
* @return {@link R }
* @return 装修页聚合数据
*/
@Inner(value = false)
@GetMapping("/decorate")
@@ -65,4 +71,16 @@ public class AppIndexController {
return R.ok(indexService.decorate(pageType));
}
/**
* 获取工作台页面装修数据。
* <p>
* 工作台内部按钮权限复用 B 端权限体系,这里不再按 App 专属菜单做二次过滤。
*
* @return 工作台页面数据
*/
@GetMapping("/workbench")
public R workbench() {
return R.ok(indexService.workbench());
}
}
@@ -30,6 +30,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* App 手机验证码接口。
*
* @author lengleng
* @date 2018/11/14
* <p>
@@ -44,6 +46,12 @@ public class AppMobileController {
private final AppMobileService appMobileService;
/**
* 发送 App 手机验证码。
*
* @param mobile 手机号
* @return 发送结果
*/
@Inner(value = false)
@GetMapping("/{mobile}")
public R sendSmsCode(@PathVariable String mobile) {
@@ -3,8 +3,11 @@ package com.pig4cloud.pigx.app.controller;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
import com.pig4cloud.pigx.app.api.enums.PageTypeEnums;
import com.pig4cloud.pigx.app.api.vo.AppDecorateVO;
import com.pig4cloud.pigx.app.service.AppPageService;
import com.pig4cloud.pigx.app.service.AppTabbarService;
import com.pig4cloud.pigx.common.core.util.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@@ -17,7 +20,10 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 页面管理
* App 装修页面后台管理接口。
* <p>
* 页面表只保存页面装修组件 JSON,底部 Tabbar 由 {@code app_tabbar} 独立维护;
* 查询装修页时会聚合 Tabbar,便于后台预览。
*
* @author pig
* @date 2023-06-07 16:32:35
@@ -31,24 +37,35 @@ public class AppPageController {
private final AppPageService pageService;
private final AppTabbarService tabbarService;
/**
* 查询文章资讯
* 按页面类型查询后台装修页配置。
* <p>
* 返回结构保持页面字段在顶层,并额外携带完整 Tabbar 列表,用于后台装修预览和 Tabbar 设置区初始化。
*
* @param pageType pageType
* @return R
* @param pageType 页面类型,见 {@link PageTypeEnums}
* @return 页面装修数据和完整 Tabbar 列表
*/
@Operation(summary = "通过页面类型查询", description = "通过页面类型查询")
@GetMapping("/{pageType}")
public R getByPageType(@PathVariable("pageType") Long pageType) {
AppPageEntity appPage = pageService.getOne(Wrappers.<AppPageEntity>lambdaQuery().eq(AppPageEntity::getPageType, pageType));
return R.ok(appPage);
public R getByPageType(@PathVariable("pageType") Integer pageType) {
AppPageEntity appPage = pageService.getOne(Wrappers.<AppPageEntity>lambdaQuery()
.eq(AppPageEntity::getPageType, pageType)
.orderByDesc(AppPageEntity::getCreateTime), false);
List<AppTabbarEntity> tabbarList = tabbarService
.list(Wrappers.<AppTabbarEntity>lambdaQuery().orderByAsc(AppTabbarEntity::getSortOrder).orderByAsc(AppTabbarEntity::getId));
return R.ok(AppDecorateVO.of(appPage, tabbarList));
}
/**
* 更新
* 保存页面装修数据。
* <p>
* 同一页面类型只保留一份最新配置;保存时先删除旧记录,再写入新记录。
* Tabbar 不随页面一起保存,需通过 {@link AppTabbarController} 单独维护。
*
* @param page 页面
* @return {@link R }
* @return 保存结果
*/
@Operation(summary = "更新页面", description = "更新页面")
@PutMapping
@@ -58,7 +75,7 @@ public class AppPageController {
.list(Wrappers.<AppPageEntity>lambdaQuery().eq(AppPageEntity::getPageType, page.getPageType()));
if (CollUtil.isNotEmpty(pageEntityList)) {
// 删掉原来类型的页面数据
// app_page 按页面类型覆盖保存,避免同一页面类型存在多份装修数据。
pageService.removeByIds(pageEntityList.stream().map(AppPageEntity::getId).toList());
}
@@ -1,185 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.app.api.entity.AppRole;
import com.pig4cloud.pigx.app.api.vo.AppRoleExcelVO;
import com.pig4cloud.pigx.app.service.AppRoleService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.excel.annotation.RequestExcel;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* app角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/approle")
@Tag(description = "approle", name = "app角色表管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class AppRoleController {
private final AppRoleService appRoleService;
/**
* 分页查询
*
* @param page 分页对象
* @param appRole app角色表
* @return
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
public R getAppRolePage(Page page, AppRole appRole) {
return R.ok(appRoleService.page(page, Wrappers.<AppRole>lambdaQuery()
.like(StrUtil.isNotBlank(appRole.getRoleName()), AppRole::getRoleName, appRole.getRoleName())));
}
/**
* 查询全部角色
*
* @return
*/
@Operation(summary = "查询全部", description = "查询全部")
@GetMapping("/list")
public R list() {
return R.ok(appRoleService.list(Wrappers.emptyWrapper()));
}
/**
* 通过id查询app角色表
*
* @param roleId id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{roleId}")
public R getById(@PathVariable("roleId") Long roleId) {
return R.ok(appRoleService.getById(roleId));
}
/**
* 通过roleName查询app角色表
*
* @param roleName roleName
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/details/{roleName}")
public R getByUserName(@PathVariable("roleName") String roleName) {
return R.ok(appRoleService.getOne(Wrappers.<AppRole>lambdaQuery().eq(AppRole::getRoleName, roleName)));
}
/**
* 通过roleCode查询app角色表
*
* @param roleCode roleCode
* @return R
*/
@Operation(summary = "通过roleCode查询", description = "通过roleCode查询")
@GetMapping("/detailsByCode/{roleCode}")
public R getByPhone(@PathVariable("roleCode") String roleCode) {
return R.ok(appRoleService.getOne(Wrappers.<AppRole>lambdaQuery().eq(AppRole::getRoleCode, roleCode)));
}
/**
* 新增app角色表
*
* @param appRole app角色表
* @return R
*/
@Operation(summary = "新增app角色表", description = "新增app角色表")
@SysLog("新增app角色表")
@PostMapping
@HasPermission("app_approle_add")
public R save(@RequestBody AppRole appRole) {
return R.ok(appRoleService.save(appRole));
}
/**
* 修改app角色表
*
* @param appRole app角色表
* @return R
*/
@Operation(summary = "修改app角色表", description = "修改app角色表")
@SysLog("修改app角色表")
@PutMapping
@HasPermission("app_approle_edit")
public R updateById(@RequestBody AppRole appRole) {
return R.ok(appRoleService.updateById(appRole));
}
/**
* 通过ids批量删除app角色表
*
* @param ids roleIds
* @return R
*/
@Operation(summary = "通过ids批量删除app角色表", description = "通过ids批量删除app角色表")
@SysLog("通过ids批量删除app角色表")
@DeleteMapping
@HasPermission("app_approle_del")
public R removeById(@RequestBody Long[] ids) {
return R.ok(appRoleService.deleteRoleByIds(ids));
}
/**
* 导出excel 表格
*
* @param appRole 查询条件
* @return excel 文件流
*/
@ResponseExcel
@GetMapping("/export")
@HasPermission("app_approle_export")
public List<AppRole> export(AppRole appRole) {
return appRoleService.list(Wrappers.query(appRole));
}
/**
* 导入角色
*
* @param excelVOList 角色列表
* @param bindingResult 错误信息列表
* @return ok fail
*/
@PostMapping("/import")
@HasPermission("app_approle_export")
public R importRole(@RequestExcel List<AppRoleExcelVO> excelVOList, BindingResult bindingResult) {
return appRoleService.importRole(excelVOList, bindingResult);
}
}
@@ -0,0 +1,105 @@
package com.pig4cloud.pigx.app.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pig4cloud.pigx.admin.api.entity.SysRole;
import com.pig4cloud.pigx.admin.api.feign.RemoteRoleService;
import com.pig4cloud.pigx.app.api.dto.AppRoleTabbarDTO;
import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
import com.pig4cloud.pigx.app.service.AppRoleTabbarService;
import com.pig4cloud.pigx.app.service.AppTabbarService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* App 角色底部导航授权管理接口。
* <p>
* 该接口只维护「系统角色 -> App Tabbar」的授权关系。角色本身仍来自 upms 的 {@code sys_role},
* App 端运行时根据登录 token 中的角色 ID 读取 {@code app_role_tabbar},不跨库 join {@code sys_user_role}。
*
* @author lengleng
* @date 2026-05-09
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/role-tabbar")
@Tag(description = "role-tabbar", name = "App角色底部导航授权")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class AppRoleTabbarController {
private final RemoteRoleService remoteRoleService;
private final AppTabbarService appTabbarService;
private final AppRoleTabbarService appRoleTabbarService;
/**
* 查询可授权的系统角色列表。
* <p>
* 角色来源于 upms 远程接口,前端可按角色名称做轻量过滤,用于角色 Tabbar 授权页面。
*
* @param roleName 角色名称关键字,可为空
* @return 系统角色列表
*/
@GetMapping("/roles")
@HasPermission("app_role_tabbar_view")
public R<List<SysRole>> roles(@RequestParam(required = false) String roleName) {
R<List<SysRole>> roles = remoteRoleService.getAllRole();
if (!roles.isOk() || roles.getData() == null || StrUtil.isBlank(roleName)) {
return roles;
}
return R.ok(roles.getData()
.stream()
.filter(role -> StrUtil.containsIgnoreCase(role.getRoleName(), roleName))
.toList());
}
/**
* 查询可授权的 App 底部导航列表。
* <p>
* 返回完整 Tabbar 列表,公开 Tabbar 前端可固定勾选,登录可见 Tabbar 由角色授权控制。
*
* @return App Tabbar 列表
*/
@GetMapping("/tabbar")
@HasPermission("app_role_tabbar_view")
public R<List<AppTabbarEntity>> tabbars() {
return R.ok(appTabbarService.list(Wrappers.<AppTabbarEntity>lambdaQuery()
.orderByAsc(AppTabbarEntity::getSortOrder)));
}
/**
* 查询指定角色已授权的 Tabbar ID。
*
* @param roleId 系统角色 ID
* @return 已授权的 Tabbar ID 列表
*/
@GetMapping("/{roleId}")
@HasPermission("app_role_tabbar_view")
public R<List<Long>> roleTabbars(@PathVariable Long roleId) {
return R.ok(appRoleTabbarService.listTabbarIdsByRoleId(roleId));
}
/**
* 保存角色 Tabbar 授权。
* <p>
* 保存采用覆盖语义:先清空该角色原有授权,再写入本次提交的 Tabbar ID 列表。
*
* @param dto 角色 Tabbar 授权请求
* @return 保存结果
*/
@PutMapping
@HasPermission("app_role_tabbar_edit")
public R<Boolean> saveRoleTabbars(@Valid @RequestBody AppRoleTabbarDTO dto) {
return R.ok(appRoleTabbarService.saveRoleTabbars(dto));
}
}
@@ -1,139 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.controller;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.app.api.entity.AppSocialDetails;
import com.pig4cloud.pigx.app.service.AppSocialDetailsService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.core.util.ValidGroup;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/appsocial")
@AllArgsConstructor
@Tag(description = "social", name = "三方账号管理模块")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class AppSocialDetailsController {
private final AppSocialDetailsService appSocialDetailsService;
/**
* 社交登录账户简单分页查询
* @param page 分页对象
* @param appSocialDetails 社交登录
* @return
*/
@GetMapping("/page")
public R getSocialDetailsPage(Page page, AppSocialDetails appSocialDetails) {
return R.ok(appSocialDetailsService.page(page, Wrappers.query(appSocialDetails)));
}
/**
* 信息
* @param id id
* @return R
*/
@GetMapping("/{id}")
public R getinfo(@PathVariable("id") Long id) {
return R.ok(appSocialDetailsService.getById(id));
}
/**
* 保存
* @param appSocialDetails
* @return R
*/
@SysLog("保存三方信息")
@PostMapping
@HasPermission("app_social_details_add")
public R save(@Valid @RequestBody AppSocialDetails appSocialDetails) {
return R.ok(appSocialDetailsService.save(appSocialDetails));
}
/**
* 修改
* @param appSocialDetails
* @return R
*/
@SysLog("修改三方信息")
@PutMapping
@HasPermission("app_social_details_edit")
public R updateById(@Validated({ ValidGroup.Update.class }) @RequestBody AppSocialDetails appSocialDetails) {
appSocialDetailsService.updateById(appSocialDetails);
return R.ok(Boolean.TRUE);
}
/**
* 删除
* @param ids
* @return R
*/
@SysLog("删除三方信息")
@DeleteMapping
@HasPermission("app_social_details_del")
public R removeById(@RequestBody Long[] ids) {
return R.ok(appSocialDetailsService.removeBatchByIds(CollUtil.toList(ids)));
}
/**
* 通过社交账号、手机号查询用户、角色信息
* @param inStr appid@code
* @return
*/
@Inner
@GetMapping("/info/{inStr}")
public R getUserInfo(@PathVariable String inStr) {
return R.ok(appSocialDetailsService.getUserInfo(inStr));
}
/**
* 绑定社交账号
* @param state 类型
* @param code code
* @return
*/
@PostMapping("/bind")
public R bindSocial(String state, String code) {
return R.ok(appSocialDetailsService.bindSocial(state, code));
}
/**
* 导出
*/
@GetMapping("/export")
@ResponseExcel
public List<AppSocialDetails> export(AppSocialDetails appSocialDetails) {
return appSocialDetailsService.list(Wrappers.query(appSocialDetails));
}
}
@@ -2,7 +2,9 @@ package com.pig4cloud.pigx.app.controller;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pig4cloud.pigx.app.api.entity.AppRoleTabbar;
import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
import com.pig4cloud.pigx.app.service.AppRoleTabbarService;
import com.pig4cloud.pigx.app.service.AppTabbarService;
import com.pig4cloud.pigx.common.core.util.R;
import io.swagger.v3.oas.annotations.Operation;
@@ -15,7 +17,10 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 底部导航
* App 底部导航后台管理接口。
* <p>
* Tabbar 是移动端底部入口的数据源,图标字段保存 Wot UI 图标名;
* 是否登录可见由 {@code loginFlag} 控制,登录后再叠加角色授权。
*
* @author pig
* @date 2023-06-07 16:32:35
@@ -29,20 +34,40 @@ public class AppTabbarController {
private final AppTabbarService tabbarService;
private final AppRoleTabbarService roleTabbarService;
/**
* 查询导航列表
* 查询全部底部导航。
* <p>
* 后台维护页面使用完整列表;移动端运行时请走 {@code /app/index/config},
* 由运行时接口按公开/角色授权规则过滤。
*
* @return R
* @return 完整 Tabbar 列表
*/
@Operation(summary = "查询导航列表", description = "查询导航列表")
@GetMapping("/list")
public R list() {
return R.ok(tabbarService.list(Wrappers.<AppTabbarEntity>lambdaQuery().orderByAsc(AppTabbarEntity::getSortOrder)));
return R.ok(tabbarService.list(
Wrappers.<AppTabbarEntity>lambdaQuery().orderByAsc(AppTabbarEntity::getSortOrder).orderByAsc(AppTabbarEntity::getId)));
}
/**
* 批量保存底部导航。
* <p>
* 前端提交的是当前完整 Tabbar 列表。这里会删除数据库中已不存在的 Tabbar,
* 并同步清理角色授权关系,避免 {@code app_role_tabbar} 残留无效 tabbar_id。
*
* @param tabbarEntityList 当前完整 Tabbar 配置
* @return 保存结果
*/
@Operation(summary = "更新导航", description = "更新导航")
@PutMapping
public R update(@RequestBody List<AppTabbarEntity> tabbarEntityList) {
tabbarEntityList.forEach(tabbar -> {
if (tabbar.getLoginFlag() == null) {
tabbar.setLoginFlag("1");
}
});
// 删除不在新增范围的导航菜单
List<Long> idList = tabbarService.list().stream().map(AppTabbarEntity::getId).toList();
List<Long> newIdList = tabbarEntityList.stream().map(AppTabbarEntity::getId).toList();
@@ -53,9 +78,10 @@ public class AppTabbarController {
// 如果有需要删除的菜单,则批量删除
if (CollUtil.isNotEmpty(idsToRemove)) {
tabbarService.removeBatchByIds(idsToRemove);
roleTabbarService.remove(Wrappers.<AppRoleTabbar>lambdaQuery().in(AppRoleTabbar::getTabbarId, idsToRemove));
}
return R.ok(tabbarService.saveOrUpdateBatch(tabbarEntityList));
}
}
}
@@ -1,215 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.app.api.dto.AppUserDTO;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import com.pig4cloud.pigx.app.api.enums.AppErrorCodes;
import com.pig4cloud.pigx.app.api.vo.AppUserExcelVO;
import com.pig4cloud.pigx.app.service.AppUserService;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum;
import com.pig4cloud.pigx.common.core.util.MsgUtils;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.excel.annotation.RequestExcel;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* app用户表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/appuser")
@Tag(description = "appuser", name = "app用户表管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class AppUserController {
private final AppUserService appUserService;
@Inner
@GetMapping("/info/{username}")
public R info(@PathVariable String username) {
AppUser user = appUserService.getOne(Wrappers.<AppUser>query()
.lambda()
.eq(AppUser::getUsername, username)
.or()
.eq(AppUser::getPhone, username));
if (user == null) {
return R.failed(MsgUtils.getMessage(AppErrorCodes.APP_USER_USERINFO_EMPTY, username));
}
return R.ok(appUserService.findUserInfo(user));
}
/**
* 获取当前用户全部信息
*
* @return 用户信息
*/
@GetMapping(value = {"/info"})
public R info() {
String username = SecurityUtils.getUser().getUsername();
AppUser user = appUserService.getOne(Wrappers.<AppUser>query().lambda().eq(AppUser::getUsername, username));
if (user == null) {
return R.failed(MsgUtils.getMessage(AppErrorCodes.APP_USER_QUERY_ERROR));
}
return R.ok(appUserService.findUserInfo(user));
}
/**
* 分页查询
*
* @param page 分页对象
* @param appUserDTO app用户表
* @return
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
public R getAppUserPage(Page page, AppUserDTO appUserDTO) {
return R.ok(appUserService.getUsersWithRolePage(page, appUserDTO));
}
/**
* 通过id查询app用户表
*
* @param userId id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/details/{userId}")
public R getById(@PathVariable("userId") Long userId) {
return R.ok(appUserService.selectUserVoById(userId));
}
/**
* @return R
*/
@Operation(summary = "通过userName查询", description = "通过userName查询")
@GetMapping("/details")
public R getByUserName(AppUser user) {
AppUser one = appUserService.getOne(Wrappers.query(user), false);
return R.ok(one == null ? null : CommonConstants.SUCCESS);
}
/**
* 新增app用户表
*
* @param appUser app用户表
* @return R
*/
@Operation(summary = "新增app用户表", description = "新增app用户表")
@SysLog("新增app用户表")
@PostMapping
@HasPermission("app_appuser_add")
public R save(@RequestBody AppUserDTO appUser) {
appUserService.saveUser(appUser);
return R.ok();
}
/**
* 修改app用户表
*
* @param appUser app用户表
* @return R
*/
@Operation(summary = "修改app用户表", description = "修改app用户表")
@SysLog("修改app用户表")
@PutMapping
@HasPermission("app_appuser_edit")
public R updateById(@RequestBody AppUserDTO appUser) {
return R.ok(appUserService.updateUser(appUser));
}
/**
* 通过id删除app用户表
*
* @param ids userIds
* @return R
*/
@Operation(summary = "通过ids删除app用户表", description = "通过ids删除app用户表")
@SysLog("通过id删除app用户表")
@DeleteMapping
@HasPermission("app_appuser_del")
public R removeById(@RequestBody Long[] ids) {
return R.ok(appUserService.deleteAppUserByIds(ids));
}
/**
* 导出excel 表格
*
* @param appUser 查询条件
* @return excel 文件流
*/
@ResponseExcel
@GetMapping("/export")
@HasPermission("app_appuser_export")
public List<AppUserExcelVO> export(AppUserDTO appUser) {
return appUserService.listUser(appUser);
}
@SysLog("修改个人信息")
@PutMapping("/edit")
public R updateUserInfo(@Valid @RequestBody AppUserDTO userDto) {
if (UserTypeEnum.TOC.getStatus().equals(SecurityUtils.getUser().getUserType())) {
userDto.setUserId(SecurityUtils.getUser().getId());
// TOC 以手机号为key
userDto.setUsername(SecurityUtils.getUser().getUsername());
}
return appUserService.updateUserInfo(userDto);
}
/**
* 导入用户
*
* @param excelVOList 用户列表
* @param bindingResult 错误信息列表
* @return R
*/
@PostMapping("/import")
@HasPermission("app_appuser_export")
public R importUser(@RequestExcel List<AppUserExcelVO> excelVOList, BindingResult bindingResult) {
return appUserService.importUser(excelVOList, bindingResult);
}
@Operation(summary = "注册app用户表", description = "注册app用户表")
@SysLog("注册app用户表")
@Inner(value = false)
@PostMapping("/register")
public R register(@RequestBody AppUserDTO appUser) {
return appUserService.registerAppUser(appUser);
}
}
@@ -1,128 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.app.api.entity.AppUserRole;
import com.pig4cloud.pigx.app.service.AppUserRoleService;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.common.security.annotation.HasPermission;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用户角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/appuserrole")
@Tag(description = "appuserrole", name = "用户角色表管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class AppUserRoleController {
private final AppUserRoleService appUserRoleService;
/**
* 分页查询
* @param page 分页对象
* @param appUserRole 用户角色表
* @return
*/
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@HasPermission("app_appuserrole_view")
public R getAppUserRolePage(Page page, AppUserRole appUserRole) {
return R.ok(appUserRoleService.page(page, Wrappers.query(appUserRole)));
}
/**
* 通过id查询用户角色表
* @param userId id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{userId}")
@HasPermission("app_appuserrole_view")
public R getById(@PathVariable("userId") Long userId) {
return R.ok(appUserRoleService.getById(userId));
}
/**
* 新增用户角色表
* @param appUserRole 用户角色表
* @return R
*/
@Operation(summary = "新增用户角色表", description = "新增用户角色表")
@SysLog("新增用户角色表")
@PostMapping
@HasPermission("app_appuserrole_add")
public R save(@RequestBody AppUserRole appUserRole) {
return R.ok(appUserRoleService.save(appUserRole));
}
/**
* 修改用户角色表
* @param appUserRole 用户角色表
* @return R
*/
@Operation(summary = "修改用户角色表", description = "修改用户角色表")
@SysLog("修改用户角色表")
@PutMapping
@HasPermission("app_appuserrole_edit")
public R updateById(@RequestBody AppUserRole appUserRole) {
return R.ok(appUserRoleService.updateById(appUserRole));
}
/**
* 通过id删除用户角色表
* @param userId id
* @return R
*/
@Operation(summary = "通过id删除用户角色表", description = "通过id删除用户角色表")
@SysLog("通过id删除用户角色表")
@DeleteMapping("/{userId}")
@HasPermission("app_appuserrole_del")
public R removeById(@PathVariable Long userId) {
return R.ok(appUserRoleService.removeById(userId));
}
/**
* 导出excel 表格
* @param appUserRole 查询条件
* @return excel 文件流
*/
@ResponseExcel
@GetMapping("/export")
@HasPermission("app_appuserrole_export")
public List<AppUserRole> export(AppUserRole appUserRole) {
return appUserRoleService.list(Wrappers.query(appUserRole));
}
}
@@ -1,53 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.handler;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
/**
* @author lengleng
* @date 2018/11/18
*/
public abstract class AbstractLoginHandler implements LoginHandler {
/***
* 数据合法性校验
* @param loginStr 通过用户传入获取唯一标识
* @return 默认不校验
*/
@Override
public Boolean check(String loginStr) {
return true;
}
/**
* 处理方法
* @param loginStr 登录参数
* @return
*/
@Override
public AppUserInfo handle(String loginStr) {
if (!check(loginStr)) {
return null;
}
String identify = identify(loginStr);
return info(identify);
}
}
@@ -1,69 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.handler;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.app.api.entity.AppUser;
/**
* @author lengleng
* @date 2018/11/18
* <p>
* 登录处理器
*/
public interface LoginHandler {
/***
* 数据合法性校验
* @param loginStr 通过用户传入获取唯一标识
* @return
*/
Boolean check(String loginStr);
/**
* 通过用户传入获取唯一标识
* @param loginStr
* @return
*/
String identify(String loginStr);
/**
* 通过openId 获取用户信息
* @param identify
* @return
*/
AppUserInfo info(String identify);
/**
* 处理方法
* @param loginStr 登录参数
* @return
*/
AppUserInfo handle(String loginStr);
/**
* 绑定逻辑
* @param user 用户实体
* @param identify 渠道返回唯一标识
* @return
*/
default Boolean bind(AppUser user, String identify) {
return false;
}
}
@@ -1,122 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.handler;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.app.api.entity.AppSocialDetails;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import com.pig4cloud.pigx.app.mapper.AppSocialDetailsMapper;
import com.pig4cloud.pigx.app.service.AppUserService;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
import com.pig4cloud.pigx.common.core.exception.CheckedException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* @author lengleng
* @date 2019年11月02日
* <p>
* 微信小程序
*/
@Slf4j
@Component("APP-MINI")
@AllArgsConstructor
public class MiniAppLoginHandler extends AbstractLoginHandler {
private final AppUserService appUserService;
private final AppSocialDetailsMapper appSocialDetailsMapper;
/**
* 小程序登录传入code
* <p>
* 通过code 调用qq 获取唯一标识
*
* @param code
* @return
*/
@Override
public String identify(String code) {
AppSocialDetails condition = new AppSocialDetails();
condition.setType(LoginTypeEnum.MINI_APP.getType());
AppSocialDetails socialDetails = appSocialDetailsMapper.selectOne(new QueryWrapper<>(condition));
String url = String.format(SecurityConstants.MINI_APP_AUTHORIZATION_CODE_URL, socialDetails.getAppId(),
socialDetails.getAppSecret(), code);
String result = HttpUtil.get(url);
log.debug("微信小程序响应报文:{}", result);
if (JSONUtil.parseObj(result).containsKey("openid")) {
return JSONUtil.parseObj(result).get("openid").toString();
} else {
log.error("微信小程序登录失败:{}", result);
throw new CheckedException("微信小程序登录失败");
}
}
/**
* openId 获取用户信息
*
* @param openId
* @return
*/
@Override
public AppUserInfo info(String openId) {
AppUser user = appUserService.getOne(Wrappers.<AppUser>query().lambda().eq(AppUser::getWxOpenid, openId));
// 未注册用户自动注册
if (Objects.isNull(user)) {
log.info("微信小程序未绑定:{},创建新的用户", openId);
AppUser appUser = new AppUser();
appUser.setWxOpenid(openId);
appUser.setUsername(openId);
appUser.setCreateBy(openId);
appUser.setUpdateBy(openId);
appUserService.saveOrUpdate(appUser);
AppUserInfo appUserDTO = new AppUserInfo();
appUserDTO.setAppUser(appUser);
return appUserDTO;
}
return appUserService.findUserInfo(user);
}
/**
* 绑定逻辑
*
* @param user 用户实体
* @param identify 渠道返回唯一标识
* @return
*/
@Override
public Boolean bind(AppUser user, String identify) {
user.setWxOpenid(identify);
appUserService.updateById(user);
return true;
}
}
@@ -1,93 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.handler;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import com.pig4cloud.pigx.app.mapper.AppUserMapper;
import com.pig4cloud.pigx.app.service.AppUserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* @author lengleng
* @date 2018/11/18
*/
@Slf4j
@Component("APP-SMS")
@AllArgsConstructor
public class SmsLoginHandler extends AbstractLoginHandler {
private final AppUserService appUserService;
/**
* 验证码登录传入为手机号 不用不处理
* @param mobile
* @return
*/
@Override
public String identify(String mobile) {
return mobile;
}
/**
* 通过mobile 获取用户信息
* @param identify
* @return
*/
@Override
public AppUserInfo info(String identify) {
AppUser user = appUserService.getOne(Wrappers.<AppUser>query().lambda().eq(AppUser::getPhone, identify));
// 未注册用户自动注册
if (Objects.isNull(user)) {
log.info("手机号未注册:{}", identify);
AppUser appUser = new AppUser();
appUser.setUsername(identify);
appUser.setPhone(identify);
appUser.setCreateBy(identify);
appUser.setUpdateBy(identify);
appUserService.save(appUser);
AppUserInfo appUserDTO = new AppUserInfo();
appUserDTO.setAppUser(appUser);
return appUserDTO;
}
// 返回用户信息
return appUserService.findUserInfo(user);
}
/**
* 绑定逻辑
* @param user 用户实体
* @param identify 渠道返回唯一标识
* @return
*/
@Override
public Boolean bind(AppUser user, String identify) {
user.setPhone(identify);
appUserService.updateById(user);
return true;
}
}
@@ -4,6 +4,9 @@ import com.pig4cloud.pigx.app.api.entity.AppArticleCategoryEntity;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 文章分类 Mapper。
*/
@Mapper
public interface AppArticleCategoryMapper extends PigxBaseMapper<AppArticleCategoryEntity> {
@@ -4,6 +4,9 @@ import com.pig4cloud.pigx.app.api.entity.AppArticleCollectEntity;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 文章收藏 Mapper。
*/
@Mapper
public interface AppArticleCollectMapper extends PigxBaseMapper<AppArticleCollectEntity> {
@@ -4,6 +4,9 @@ import com.pig4cloud.pigx.app.api.entity.AppArticleEntity;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 文章资讯 Mapper。
*/
@Mapper
public interface AppArticleMapper extends PigxBaseMapper<AppArticleEntity> {
@@ -0,0 +1,16 @@
package com.pig4cloud.pigx.app.mapper;
import com.pig4cloud.pigx.app.api.entity.AppContactsEntity;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 通讯录 Mapper。
* <p>
* 继承 {@link PigxBaseMapper} 后可配合数据权限插件执行范围查询。
*/
@Mapper
public interface AppContactsMapper extends PigxBaseMapper<AppContactsEntity> {
}
@@ -4,6 +4,9 @@ import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* App 装修页面 Mapper。
*/
@Mapper
public interface AppPageMapper extends PigxBaseMapper<AppPageEntity> {
@@ -1,37 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.mapper;
import com.pig4cloud.pigx.app.api.entity.AppRole;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* app角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@Mapper
public interface AppRoleMapper extends PigxBaseMapper<AppRole> {
List<AppRole> listRolesByUserId(Long userId);
}
@@ -0,0 +1,16 @@
package com.pig4cloud.pigx.app.mapper;
import com.pig4cloud.pigx.app.api.entity.AppRoleTabbar;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* App 角色底部导航 Mapper。
*
* @author lengleng
* @date 2026-05-09
*/
@Mapper
public interface AppRoleTabbarMapper extends PigxBaseMapper<AppRoleTabbar> {
}
@@ -1,33 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.mapper;
import com.pig4cloud.pigx.app.api.entity.AppSocialDetails;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 系统社交登录账号表
*
* @author lengleng
* @date 2018-08-16 21:30:41
*/
@Mapper
public interface AppSocialDetailsMapper extends PigxBaseMapper<AppSocialDetails> {
}
@@ -4,6 +4,9 @@ import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* App 底部导航 Mapper。
*/
@Mapper
public interface AppTabbarMapper extends PigxBaseMapper<AppTabbarEntity> {
@@ -1,42 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.app.api.dto.AppUserDTO;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import com.pig4cloud.pigx.app.api.vo.AppUserVO;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* app用户表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@Mapper
public interface AppUserMapper extends PigxBaseMapper<AppUser> {
IPage<AppUserVO> getUserVosPage(Page page, @Param("query") AppUserDTO appUserDTO);
AppUserVO getUserVoById(Long userId);
}
@@ -1,33 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.mapper;
import com.pig4cloud.pigx.app.api.entity.AppUserRole;
import com.pig4cloud.pigx.common.data.datascope.PigxBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@Mapper
public interface AppUserRoleMapper extends PigxBaseMapper<AppUserRole> {
}
@@ -3,6 +3,9 @@ package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppArticleCategoryEntity;
/**
* 文章分类基础服务。
*/
public interface AppArticleCategoryService extends IService<AppArticleCategoryEntity> {
}
@@ -3,8 +3,19 @@ package com.pig4cloud.pigx.app.service;
import com.github.yulichang.base.MPJBaseService;
import com.pig4cloud.pigx.app.api.entity.AppArticleCollectEntity;
/**
* 文章收藏服务。
* <p>
* 收藏记录以当前登录用户为维度保存,同一用户对同一文章只允许收藏一次。
*/
public interface AppArticleCollectService extends MPJBaseService<AppArticleCollectEntity> {
/**
* 保存当前用户的文章收藏。
*
* @param appArticleCollect 收藏请求,需包含文章 ID
* @return true=保存成功,false=已收藏或未保存
*/
Boolean saveArticleCollect(AppArticleCollectEntity appArticleCollect);
}
@@ -0,0 +1,23 @@
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppContactsEntity;
/**
* 应用联系人服务接口
*
* @author lengleng
* @date 2025/05/29
*/
public interface AppContactsService extends IService<AppContactsEntity> {
/**
* 根据应用联系人实体获取分页范围
*
* @param page 分页对象
* @param contactsEntity 应用联系人实体
* @return 分页范围对象
*/
Page pageScope(Page page, AppContactsEntity contactsEntity);
}
@@ -1,11 +1,15 @@
package com.pig4cloud.pigx.app.service;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
import com.pig4cloud.pigx.app.api.vo.AppDecorateVO;
import java.util.Map;
/**
* app 页面控制
* App 运行时页面服务。
* <p>
* 负责给移动端聚合首页、装修页、工作台和 Tabbar 配置;
* 后台装修管理接口不直接走这里。
*
* @author lengleng
* @date 2023/6/8
@@ -13,21 +17,33 @@ import java.util.Map;
public interface AppIndexService {
/**
* 首页
* 获取首页基础数据。
*
* @return 首页页面记录和资讯列表
*/
Map<String, Object> index();
/**
* 配置
* @return Map<String, Object>
* 获取运行时配置。
*
* @return 当前访问身份可见的 Tabbar 等配置
*/
Map<String, Object> config();
/**
* 装修
* @param id 装修ID
* @return Map<String, Object>
* 获取指定页面类型的装修页聚合数据。
*
* @param id 页面类型
* @return 装修页聚合数据
*/
AppPageEntity decorate(Integer id);
AppDecorateVO decorate(Integer id);
/**
* 返回工作台页面实体对象。
* <p>
* 工作台按钮权限由 B 端 permission 控制,这里只返回页面装修数据。
*
* @return 工作台页面实体对象
*/
AppPageEntity workbench();
}
@@ -3,6 +3,12 @@ package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
/**
* App 装修页面基础服务。
* <p>
* 页面组件 JSON 保存在 {@link AppPageEntity#getPageData()},
* 底部 Tabbar 不保存在页面 JSON 中。
*/
public interface AppPageService extends IService<AppPageEntity> {
}
@@ -1,46 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppRole;
import com.pig4cloud.pigx.app.api.vo.AppRoleExcelVO;
import com.pig4cloud.pigx.common.core.util.R;
import org.springframework.validation.BindingResult;
import java.util.List;
/**
* app角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
public interface AppRoleService extends IService<AppRole> {
List<AppRole> findRolesByUserId(Long userId);
/**
* 删除用户的同时,把role_menu关系删除
* @param ids RoleIds
*/
Boolean deleteRoleByIds(Long[] ids);
R importRole(List<AppRoleExcelVO> excelVOList, BindingResult bindingResult);
}
@@ -0,0 +1,40 @@
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.dto.AppRoleTabbarDTO;
import com.pig4cloud.pigx.app.api.entity.AppRoleTabbar;
import java.util.List;
/**
* App 角色底部导航服务,管理系统角色与 App 底部导航的授权关系。
*
* @author lengleng
*/
public interface AppRoleTabbarService extends IService<AppRoleTabbar> {
/**
* 查询指定角色已授权的底部导航 ID 列表。
*
* @param roleId 系统角色 ID
* @return 底部导航 ID 列表
*/
List<Long> listTabbarIdsByRoleId(Long roleId);
/**
* 查询指定角色列表已授权的底部导航 ID 列表。
*
* @param roleIds 系统角色 ID 列表
* @return 底部导航 ID 列表
*/
List<Long> listTabbarIdsByRoleIds(List<Long> roleIds);
/**
* 保存角色底部导航授权。
*
* @param dto 角色底部导航配置
* @return 操作结果
*/
Boolean saveRoleTabbars(AppRoleTabbarDTO dto);
}
@@ -1,47 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.app.api.entity.AppSocialDetails;
/**
* 系统社交登录账号表
*
* @author lengleng
* @date 2018-08-16 21:30:41
*/
public interface AppSocialDetailsService extends IService<AppSocialDetails> {
/**
* 绑定社交账号
* @param state 类型
* @param code code
* @return
*/
Boolean bindSocial(String state, String code);
/**
* 根据入参查询用户信息
* @param inStr
* @return
*/
AppUserInfo getUserInfo(String inStr);
}
@@ -3,6 +3,9 @@ package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
/**
* App 底部导航基础服务。
*/
public interface AppTabbarService extends IService<AppTabbarEntity> {
}
@@ -1,31 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppUserRole;
/**
* 用户角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
public interface AppUserRoleService extends IService<AppUserRole> {
}
@@ -1,61 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.pigx.app.api.dto.AppUserDTO;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import com.pig4cloud.pigx.app.api.vo.AppUserExcelVO;
import com.pig4cloud.pigx.app.api.vo.AppUserVO;
import com.pig4cloud.pigx.common.core.util.R;
import org.springframework.validation.BindingResult;
import java.util.List;
/**
* app用户表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
public interface AppUserService extends IService<AppUser> {
Boolean updateUser(AppUserDTO appUser);
Boolean saveUser(AppUserDTO appUser);
List<AppUserExcelVO> listUser(AppUserDTO appUser);
IPage getUsersWithRolePage(Page page, AppUserDTO appUserDTO);
AppUserInfo findUserInfo(AppUser user);
R updateUserInfo(AppUserDTO userDto);
AppUserVO selectUserVoById(Long userId);
Boolean deleteAppUserByIds(Long[] ids);
R importUser(List<AppUserExcelVO> excelVOList, BindingResult bindingResult);
R registerAppUser(AppUserDTO appUser);
}
@@ -9,7 +9,9 @@ import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import org.springframework.stereotype.Service;
/**
* 文章收藏表
* 文章收藏服务实现。
* <p>
* 收藏归属当前登录用户,保存前会校验同一用户是否已经收藏过同一文章。
*
* @author pig
* @date 2023-06-16 14:33:41
@@ -18,6 +20,12 @@ import org.springframework.stereotype.Service;
public class AppArticleCollectServiceImpl extends ServiceImpl<AppArticleCollectMapper, AppArticleCollectEntity>
implements AppArticleCollectService {
/**
* 保存当前登录用户的文章收藏。
*
* @param appArticleCollect 收藏请求,业务上只需要文章 ID,用户 ID 由登录态写入
* @return true=收藏成功,false=已经收藏
*/
@Override
public Boolean saveArticleCollect(AppArticleCollectEntity appArticleCollect) {
Long id = SecurityUtils.getUser().getId();
@@ -17,7 +17,9 @@ import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 文章资讯
* 文章资讯服务实现。
* <p>
* 负责文章列表关联分类名称、文章详情访问次数递增,以及当前用户收藏状态补充。
*
* @author pig
* @date 2023-06-07 16:32:35
@@ -30,14 +32,19 @@ public class AppArticleServiceImpl extends ServiceImpl<AppArticleMapper, AppArti
private final AppArticleCollectMapper collectMapper;
/**
* 获取文章并使阅读数+1
* @param id id
* @return
* 获取文章详情,并尝试将阅读数加 1。
* <p>
* 如果传入用户 ID,会额外查询该用户是否已收藏当前文章。
* 阅读数更新使用当前 visit 值作为条件,降低并发覆盖风险。
*
* @param id 文章 ID
* @param userId 当前用户 ID,可为空
* @return 文章详情
*/
@Override
public AppArticleEntity getArticleAndIncrById(Long id, Long userId) {
AppArticleEntity appArticleEntity = baseMapper.selectById(id);
// 查询是否收藏了
// 登录用户访问详情时,补充当前用户是否已收藏该文章。
if (Objects.nonNull(userId)) {
boolean exists = collectMapper.exists(Wrappers.<AppArticleCollectEntity>lambdaQuery()//
.eq(AppArticleCollectEntity::getArticleId, appArticleEntity.getId())//
@@ -45,10 +52,9 @@ public class AppArticleServiceImpl extends ServiceImpl<AppArticleMapper, AppArti
appArticleEntity.setCollect(exists);
}
// TODO 更新条件需要根据其他指数限制
Integer nowVisit = appArticleEntity.getVisit();
appArticleEntity.setVisit(nowVisit + 1);
// 乐观锁
// 使用旧 visit 值作为更新条件,避免并发访问时直接覆盖最新阅读数。
baseMapper.update(appArticleEntity,
Wrappers.<AppArticleEntity>lambdaQuery()
.eq(AppArticleEntity::getId, id)
@@ -57,10 +63,11 @@ public class AppArticleServiceImpl extends ServiceImpl<AppArticleMapper, AppArti
}
/**
* 分页查询文章列表 包含分类名称
* 分页查询文章列表,并关联分类名称。
*
* @param page 分页参数
* @param appArticle 文章查询条件
* @return
* @return 文章分页结果,包含分类名称 cname
*/
@Override
public Page pageAndCname(Page page, AppArticleEntity appArticle) {
@@ -0,0 +1,42 @@
package com.pig4cloud.pigx.app.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.entity.AppContactsEntity;
import com.pig4cloud.pigx.app.mapper.AppContactsMapper;
import com.pig4cloud.pigx.app.service.AppContactsService;
import com.pig4cloud.pigx.common.data.datascope.DataScope;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import org.springframework.stereotype.Service;
/**
* 通讯录服务实现类
* <p>
* 通讯录列表会结合系统数据权限,只返回当前登录用户可见范围内的数据。
*
* @author lengleng
* @date 2025/05/29
*/
@Service
public class AppContactsServiceImpl extends ServiceImpl<AppContactsMapper, AppContactsEntity> implements AppContactsService {
/**
* 根据查询条件分页查询通讯录,并应用数据权限范围。
*
* @param page 分页对象
* @param appContacts 应用联系人实体
* @return 分页范围对象
*/
@Override
public Page pageScope(Page page, AppContactsEntity appContacts) {
LambdaQueryWrapper<AppContactsEntity> wrapper = Wrappers.lambdaQuery();
wrapper.like(StrUtil.isNotBlank(appContacts.getContactName()), AppContactsEntity::getContactName, appContacts.getContactName());
wrapper.like(StrUtil.isNotBlank(appContacts.getContactPhone()), AppContactsEntity::getContactPhone, appContacts.getContactPhone());
DataScope dataScope = new DataScope();
dataScope.setUsername(SecurityUtils.getUser().getUsername());
return baseMapper.selectPageByScope(page, wrapper, dataScope);
}
}
@@ -5,10 +5,14 @@ import com.pig4cloud.pigx.app.api.entity.AppArticleEntity;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
import com.pig4cloud.pigx.app.api.enums.PageTypeEnums;
import com.pig4cloud.pigx.app.api.vo.AppDecorateVO;
import com.pig4cloud.pigx.app.mapper.AppArticleMapper;
import com.pig4cloud.pigx.app.mapper.AppPageMapper;
import com.pig4cloud.pigx.app.mapper.AppTabbarMapper;
import com.pig4cloud.pigx.app.service.AppIndexService;
import com.pig4cloud.pigx.app.service.AppRoleTabbarService;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -17,24 +21,33 @@ import java.util.List;
import java.util.Map;
/**
* app 页面控制
* App首页服务实现类
* 实现AppIndexService接口,提供首页数据、导航栏配置以及页面配置等功能。
* <p>
* 该实现面向移动端运行时:Tabbar 会按公开/登录角色授权规则过滤,
* 页面装修数据则保持原始组件 JSON,不在这里做页面组件级权限裁剪。
*
* @author lengleng
* @date 2023/6/8
*/
@Service
@RequiredArgsConstructor
public class AppIndexServiceImpl implements AppIndexService {
private final AppArticleMapper appArticleMapper;
private final AppTabbarMapper appTabbarMapper;
private final AppPageMapper appPageMapper;
private final AppArticleMapper appArticleMapper;
private final AppRoleTabbarService appRoleTabbarService;
/**
* 商城首页 首页数据
* 获取商城首页基础数据。
* <p>
* 旧版首页接口返回首页装修页记录和资讯列表;新版装修页推荐使用 {@link #decorate(Integer)}。
*
* @return 首页页面数据和资讯列表
*/
@Override
public Map<String, Object> index() {
@@ -44,6 +57,7 @@ public class AppIndexServiceImpl implements AppIndexService {
.orderByDesc(AppPageEntity::getCreateTime),
false
);
List<AppArticleEntity> articleList = appArticleMapper
.selectList(Wrappers.<AppArticleEntity>lambdaQuery().orderByDesc(AppArticleEntity::getSort));
response.put("pages", appPageEntity);
@@ -52,29 +66,74 @@ public class AppIndexServiceImpl implements AppIndexService {
}
/**
* 导航栏配置
* 获取运行时配置。
* <p>
* 当前只包含 Tabbar,并按 {@link #listAuthorizedTabbar()} 的规则过滤。
*
* @return Map<String, Object>
* @return 运行时配置集合
*/
@Override
public Map<String, Object> config() {
Map<String, Object> response = new LinkedHashMap<>();
List<AppTabbarEntity> tabbarEntityList = appTabbarMapper
.selectList(Wrappers.<AppTabbarEntity>lambdaQuery().orderByAsc(AppTabbarEntity::getSortOrder));
response.put("tabbar", tabbarEntityList);
response.put("tabbar", listAuthorizedTabbar());
return response;
}
/**
* 根据类型查询页面配置
* 根据页面类型获取最新装修页,并附加当前访问身份可见的 Tabbar。
*
* @param pageType 页面类型
* @return {@link AppPageEntity }
* @return 装修页聚合数据
*/
@Override
public AppPageEntity decorate(Integer pageType) {
return appPageMapper.selectOne(Wrappers.<AppPageEntity>lambdaQuery().eq(AppPageEntity::getPageType, pageType)
.orderByDesc(AppPageEntity::getCreateTime), false);
public AppDecorateVO decorate(Integer pageType) {
AppPageEntity appPageEntity = appPageMapper.selectOne(
Wrappers.<AppPageEntity>lambdaQuery()
.eq(AppPageEntity::getPageType, pageType)
.orderByDesc(AppPageEntity::getCreateTime),
false
);
return AppDecorateVO.of(appPageEntity, listAuthorizedTabbar());
}
/**
* 获取工作台页面数据。
* <p>
* 工作台内部按钮权限复用系统菜单 permission,这里不再读取 App 专属菜单授权。
*
* @return 工作台页面实体
*/
@Override
public AppPageEntity workbench() {
return appPageMapper.selectOne(
Wrappers.<AppPageEntity>lambdaQuery()
.eq(AppPageEntity::getPageType, PageTypeEnums.WORKBENCH.getPageType())
.orderByDesc(AppPageEntity::getCreateTime),
false
);
}
/**
* 查询当前访问身份可见的 Tabbar。
* <p>
* 匿名访问只返回 {@code loginFlag=0} 的公开 Tabbar;登录访问从 token authorities
* 解析角色 ID,再读取 {@code app_role_tabbar} 中授权的 Tabbar,避免跨库 join upms 的
* {@code sys_user_role}。
*
* @return 当前可见的 Tabbar 列表
*/
private List<AppTabbarEntity> listAuthorizedTabbar() {
List<Long> roleIds = SecurityUtils.getUser() == null ? List.of() : SecurityUtils.getRoleIds();
List<Long> tabbarIds = appRoleTabbarService.listTabbarIdsByRoleIds(roleIds);
return appTabbarMapper.selectList(Wrappers.<AppTabbarEntity>lambdaQuery()
.and(wrapper -> {
wrapper.eq(AppTabbarEntity::getLoginFlag, CommonConstants.STATUS_NORMAL);
if (!tabbarIds.isEmpty()) {
wrapper.or().in(AppTabbarEntity::getId, tabbarIds);
}
})
.orderByAsc(AppTabbarEntity::getSortOrder)
.orderByAsc(AppTabbarEntity::getId));
}
}
@@ -33,6 +33,11 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* App 手机验证码服务实现。
* <p>
* 验证码按 {@code 验证码缓存前缀 + 登录类型 + 手机号} 写入 Redis,
* 过期时间使用统一安全常量配置。
*
* @author lengleng
* @date 2018/11/14
* <p>
@@ -44,10 +49,13 @@ import org.springframework.stereotype.Service;
public class AppMobileServiceImpl implements AppMobileService {
/**
* 发送手机验证码 TODO: 调用短信网关发送验证码,测试返回前端
* 发送手机验证码。
* <p>
* 如果验证码仍在有效期内,则拒绝重复发送。当前实现只生成并缓存验证码,
* 后续接入短信网关时应在生成验证码后调用真实发送能力。
*
* @param mobile mobile
* @return code
* @param mobile 手机号
* @return true=生成成功,false=验证码未过期
*/
@Override
public R<Boolean> sendSmsCode(String mobile) {
@@ -1,124 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
import com.pig4cloud.pigx.app.api.entity.AppRole;
import com.pig4cloud.pigx.app.api.enums.AppErrorCodes;
import com.pig4cloud.pigx.app.api.enums.PageTypeEnums;
import com.pig4cloud.pigx.app.api.vo.AppRoleExcelVO;
import com.pig4cloud.pigx.app.mapper.AppPageMapper;
import com.pig4cloud.pigx.app.mapper.AppRoleMapper;
import com.pig4cloud.pigx.app.service.AppRoleService;
import com.pig4cloud.pigx.common.core.util.MsgUtils;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* APP角色服务实现类
* <p>
* 提供对APP角色相关的业务操作实现。
*
* @author lengleng
* @date 2025/05/29
*/
@Service
@RequiredArgsConstructor
public class AppRoleServiceImpl extends ServiceImpl<AppRoleMapper, AppRole> implements AppRoleService {
private final AppPageMapper appPageMapper;
private final AppRoleMapper appRoleMapper;
@Override
public List<AppRole> findRolesByUserId(Long userId) {
return baseMapper.listRolesByUserId(userId);
}
/**
* 删除用户的同时,把role_menu关系删除
*
* @param ids roleIds
*/
@Override
public Boolean deleteRoleByIds(Long[] ids) {
this.removeBatchByIds(CollUtil.toList(ids));
return Boolean.TRUE;
}
/**
* 导入角色
*
* @param excelVOList
* @param bindingResult
* @return
*/
@Override
public R importRole(List<AppRoleExcelVO> excelVOList, BindingResult bindingResult) {
// 通用校验获取失败的数据
List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
// 个性化校验逻辑
List<AppRole> roleList = this.list();
// 执行数据插入操作 组装 RoleDto
for (AppRoleExcelVO excel : excelVOList) {
Set<String> errorMsg = new HashSet<>();
// 检验角色名称或者角色编码是否存在
boolean existRole = roleList.stream()
.anyMatch(appRole -> excel.getRoleName().equals(appRole.getRoleName())
|| excel.getRoleCode().equals(appRole.getRoleCode()));
if (existRole) {
errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_ROLE_NAMEORCODE_EXISTING, excel.getRoleName(),
excel.getRoleCode()));
}
// 数据合法情况
if (CollUtil.isEmpty(errorMsg)) {
insertExcelRole(excel);
} else {
// 数据不合法情况
errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
}
}
if (CollUtil.isNotEmpty(errorMessageList)) {
return R.failed(errorMessageList);
}
return R.ok();
}
private void insertExcelRole(AppRoleExcelVO excel) {
AppRole appRole = new AppRole();
appRole.setRoleName(excel.getRoleName());
appRole.setRoleDesc(excel.getRoleDesc());
appRole.setRoleCode(excel.getRoleCode());
this.save(appRole);
}
}
@@ -0,0 +1,83 @@
package com.pig4cloud.pigx.app.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.dto.AppRoleTabbarDTO;
import com.pig4cloud.pigx.app.api.entity.AppRoleTabbar;
import com.pig4cloud.pigx.app.mapper.AppRoleTabbarMapper;
import com.pig4cloud.pigx.app.service.AppRoleTabbarService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
/**
* App 角色底部导航服务实现。
* <p>
* 只访问 app-server 本库的 {@code app_role_tabbar}。运行时角色来源由 Spring Security
* token authorities 提供,避免 app-server 跨库 join upms 的用户角色表。
*
* @author lengleng
* @date 2026-05-09
*/
@Service
@RequiredArgsConstructor
public class AppRoleTabbarServiceImpl extends ServiceImpl<AppRoleTabbarMapper, AppRoleTabbar>
implements AppRoleTabbarService {
@Override
public List<Long> listTabbarIdsByRoleId(Long roleId) {
return roleId == null ? List.of() : listTabbarIdsByRoleIds(List.of(roleId));
}
/**
* 根据多个角色查询可见 Tabbar。
* <p>
* 一个用户可能同时拥有多个角色,因此这里会先对角色 ID 去重,再对查询出的 Tabbar ID 去重。
*
* @param roleIds 系统角色 ID 列表
* @return 去重后的 Tabbar ID 列表
*/
@Override
public List<Long> listTabbarIdsByRoleIds(List<Long> roleIds) {
if (CollUtil.isEmpty(roleIds)) {
return List.of();
}
List<Long> distinctRoleIds = roleIds.stream().filter(Objects::nonNull).distinct().toList();
if (CollUtil.isEmpty(distinctRoleIds)) {
return List.of();
}
return this.list(Wrappers.<AppRoleTabbar>lambdaQuery().in(AppRoleTabbar::getRoleId, distinctRoleIds))
.stream()
.map(AppRoleTabbar::getTabbarId)
.filter(Objects::nonNull)
.distinct()
.toList();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveRoleTabbars(AppRoleTabbarDTO dto) {
// 覆盖保存:后台每次提交的是该角色完整授权结果,先删旧关系再写新关系。
this.remove(Wrappers.<AppRoleTabbar>lambdaQuery().eq(AppRoleTabbar::getRoleId, dto.getRoleId()));
if (CollUtil.isEmpty(dto.getTabbarIds())) {
return Boolean.TRUE;
}
List<AppRoleTabbar> records = dto.getTabbarIds()
.stream()
.filter(Objects::nonNull)
.distinct()
.map(tabbarId -> {
AppRoleTabbar roleTabbar = new AppRoleTabbar();
roleTabbar.setRoleId(dto.getRoleId());
roleTabbar.setTabbarId(tabbarId);
return roleTabbar;
})
.toList();
return this.saveBatch(records);
}
}
@@ -1,86 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.service.impl;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.app.api.entity.AppSocialDetails;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import com.pig4cloud.pigx.app.handler.LoginHandler;
import com.pig4cloud.pigx.app.mapper.AppSocialDetailsMapper;
import com.pig4cloud.pigx.app.mapper.AppUserMapper;
import com.pig4cloud.pigx.app.service.AppSocialDetailsService;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author lengleng
* @date 2018年08月16日
*/
@Slf4j
@AllArgsConstructor
@Service("appSocialDetailsService")
public class AppSocialDetailsServiceImpl extends ServiceImpl<AppSocialDetailsMapper, AppSocialDetails>
implements AppSocialDetailsService {
private final Map<String, LoginHandler> loginHandlerMap;
private final CacheManager cacheManager;
private final AppUserMapper appUserMapper;
/**
* 绑定社交账号
* @param type type
* @param code code
* @return
*/
@Override
public Boolean bindSocial(String type, String code) {
LoginHandler loginHandler = loginHandlerMap.get(type);
// 绑定逻辑
String identify = loginHandler.identify(code);
AppUser user = appUserMapper.selectById(SecurityUtils.getUser().getId());
loginHandler.bind(user, identify);
// 更新緩存
cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(user.getUsername());
return Boolean.TRUE;
}
/**
* 根据入参查询用户信息
* @param inStr TYPE@code
* @return
*/
@Override
public AppUserInfo getUserInfo(String inStr) {
String[] inStrs = inStr.split(StringPool.AT);
String type = inStrs[0];
String loginStr = inStr.substring(type.length() + 1);
return loginHandlerMap.get(type).handle(loginStr);
}
}
@@ -1,34 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.entity.AppUserRole;
import com.pig4cloud.pigx.app.mapper.AppUserRoleMapper;
import com.pig4cloud.pigx.app.service.AppUserRoleService;
import org.springframework.stereotype.Service;
/**
* 用户角色表
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@Service
public class AppUserRoleServiceImpl extends ServiceImpl<AppUserRoleMapper, AppUserRole> implements AppUserRoleService {
}
@@ -1,409 +0,0 @@
/*
* Copyright (c) 2018-2026, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.app.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.dto.AppUserDTO;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.app.api.entity.AppRole;
import com.pig4cloud.pigx.app.api.entity.AppUser;
import com.pig4cloud.pigx.app.api.entity.AppUserRole;
import com.pig4cloud.pigx.app.api.enums.AppErrorCodes;
import com.pig4cloud.pigx.app.api.vo.AppUserExcelVO;
import com.pig4cloud.pigx.app.api.vo.AppUserVO;
import com.pig4cloud.pigx.app.mapper.AppUserMapper;
import com.pig4cloud.pigx.app.service.AppRoleService;
import com.pig4cloud.pigx.app.service.AppUserRoleService;
import com.pig4cloud.pigx.app.service.AppUserService;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum;
import com.pig4cloud.pigx.common.core.util.MsgUtils;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.data.cache.RedisUtils;
import com.pig4cloud.pigx.common.excel.vo.ErrorMessage;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* app用户表服务实现类
*
* @author aeizzz
* @date 2022-12-07 09:52:03
*/
@Service
@AllArgsConstructor
@Slf4j
public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements AppUserService {
/**
* 密码编码器,用于密码加密
*/
private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
/**
* 用户角色关联服务
*/
private final AppUserRoleService appUserRoleService;
/**
* 角色服务
*/
private final AppRoleService appRoleService;
/**
* 缓存管理器
*/
private final CacheManager cacheManager;
/**
* 更新用户信息
*
* @param userDTO 用户数据传输对象
* @return 更新结果
*/
@Override
@CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username")
public Boolean updateUser(AppUserDTO userDTO) {
// 创建用户实体并复制属性
AppUser appUser = new AppUser();
BeanUtils.copyProperties(userDTO, appUser);
// 如果密码不为空,进行加密处理
if (StrUtil.isNotBlank(userDTO.getPassword())) {
appUser.setPassword(ENCODER.encode(userDTO.getPassword()));
}
// 更新用户基本信息
this.updateById(appUser);
// 删除用户与角色的关联关系
appUserRoleService.remove(Wrappers.<AppUserRole>lambdaQuery().eq(AppUserRole::getUserId, appUser.getUserId()));
// 重新建立用户与角色的关联关系
userDTO.getRole().forEach(roleId -> {
AppUserRole appUserRole = new AppUserRole();
appUserRole.setRoleId(roleId);
appUserRole.setUserId(userDTO.getUserId());
appUserRole.insert();
});
// 清除以手机号为key的用户缓存
AppUser appUserInfo = baseMapper.selectById(userDTO.getUserId());
cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(appUserInfo.getPhone());
return Boolean.TRUE;
}
/**
* 新增用户
*
* @param userDTO 用户数据传输对象
* @return 保存结果
*/
@Override
public Boolean saveUser(AppUserDTO userDTO) {
// 创建用户实体并复制属性
AppUser appUser = new AppUser();
BeanUtils.copyProperties(userDTO, appUser);
// 设置删除标志为正常状态
appUser.setDelFlag(CommonConstants.STATUS_NORMAL);
// 对密码进行加密
appUser.setPassword(ENCODER.encode(userDTO.getPassword()));
// 插入用户记录
baseMapper.insert(appUser);
// 如果角色列表不为空,建立用户与角色的关联关系
if (CollUtil.isNotEmpty(userDTO.getRole())) {
List<AppUserRole> userRoleList = userDTO.getRole().stream().map(roleId -> {
AppUserRole userRole = new AppUserRole();
userRole.setUserId(appUser.getUserId());
userRole.setRoleId(roleId);
return userRole;
}).toList();
// 批量保存用户角色关联
appUserRoleService.saveBatch(userRoleList);
}
return Boolean.TRUE;
}
/**
* 查询全部用户(用于Excel导出)
*
* @param appUser 查询条件
* @return 用户Excel视图对象列表
*/
@Override
public List<AppUserExcelVO> listUser(AppUserDTO appUser) {
// 查询所有用户
List<AppUser> appUsers = baseMapper.selectList(null);
// 将用户实体转换为Excel视图对象
List<AppUserExcelVO> appUserExcelVOS = appUsers.stream().map(item -> {
AppUserExcelVO appUserExcelVO = new AppUserExcelVO();
BeanUtils.copyProperties(item, appUserExcelVO);
return appUserExcelVO;
}).toList();
return appUserExcelVOS;
}
/**
* 分页查询用户(包含角色信息)
*
* @param page 分页参数
* @param appUserDTO 查询条件
* @return 分页结果
*/
@Override
public IPage getUsersWithRolePage(Page page, AppUserDTO appUserDTO) {
return baseMapper.getUserVosPage(page, appUserDTO);
}
/**
* 查询用户详细信息(包含角色和权限)
*
* @param user 用户对象
* @return 用户信息对象
*/
@Override
public AppUserInfo findUserInfo(AppUser user) {
// 创建用户信息对象
AppUserInfo info = new AppUserInfo();
info.setAppUser(user);
// 设置角色列表(ID数组)
List<Long> roleIds = appRoleService.findRolesByUserId(user.getUserId())
.stream()
.map(AppRole::getRoleId)
.toList();
info.setRoles(ArrayUtil.toArray(roleIds, Long.class));
// 设置权限列表(暂为空)
Set<String> permissions = new HashSet<>();
info.setPermissions(ArrayUtil.toArray(permissions, String.class));
return info;
}
/**
* 更新用户个人信息
*
* @param userDTO 用户数据传输对象
* @return 操作结果
*/
@Override
@CacheEvict(value = CacheConstants.USER_DETAILS_MINI, key = "#userDTO.username")
public R updateUserInfo(AppUserDTO userDTO) {
// 获取当前登录用户
PigxUser user = SecurityUtils.getUser();
// C端客户修改手机号需要验证短信验证码
if (UserTypeEnum.TOC.getStatus().equals(user.getUserType()) && StrUtil.isNotBlank(userDTO.getPhone())) {
// 构建验证码缓存key
String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT
+ userDTO.getPhone();
// 获取缓存中的验证码
String codeObj = RedisUtils.get(key);
// 验证码不匹配,返回错误
if (!userDTO.getMobileCode().equals(codeObj)) {
return R.failed("验证码错误");
}
}
// 更新密码(如果提供了新密码)
if (StrUtil.isNotBlank(userDTO.getPassword())) {
userDTO.setPassword(ENCODER.encode(userDTO.getPassword()));
}
// 获取原用户信息并更新
AppUser appUser = baseMapper.selectById(userDTO.getUserId());
BeanUtils.copyProperties(userDTO, appUser);
// 清除以手机号为key的用户缓存
cacheManager.getCache(CacheConstants.USER_DETAILS_MINI).evict(user.getPhone());
return R.ok(this.updateById(appUser));
}
/**
* 根据用户ID查询用户视图对象
*
* @param userId 用户ID
* @return 用户视图对象
*/
@Override
public AppUserVO selectUserVoById(Long userId) {
return baseMapper.getUserVoById(userId);
}
/**
* 批量删除用户
*
* @param ids 用户ID数组
* @return 删除结果
*/
@Override
public Boolean deleteAppUserByIds(Long[] ids) {
// 获取缓存对象
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI);
// 清除用户缓存
for (AppUser appUser : baseMapper.selectByIds(CollUtil.toList(ids))) {
cache.evict(appUser.getUsername());
}
// 删除用户角色关联关系
this.appUserRoleService
.remove(Wrappers.<AppUserRole>lambdaQuery().in(AppUserRole::getUserId, CollUtil.toList(ids)));
// 删除用户记录
this.removeBatchByIds(CollUtil.toList(ids));
return Boolean.TRUE;
}
/**
* 注册APP用户
*
* @param appUser 用户数据传输对象
* @return 操作结果
*/
@Override
public R registerAppUser(AppUserDTO appUser) {
// 检查手机号是否已被注册
List<AppUser> appUserList = baseMapper
.selectList(Wrappers.<AppUser>lambdaQuery().eq(AppUser::getPhone, appUser.getPhone()));
if (CollUtil.isNotEmpty(appUserList)) {
return R.failed("手机号已注册,请使用验证码直接登录");
}
// 验证短信验证码
String key = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT
+ appUser.getPhone();
String codeObj = RedisUtils.get(key);
if (!appUser.getMobileCode().equals(codeObj)) {
return R.failed("验证码错误");
}
// 创建用户并保存
AppUser app = new AppUser();
BeanUtils.copyProperties(appUser, app);
appUser.setUsername(app.getPhone()); // 默认使用手机号作为用户名
appUser.setDelFlag(CommonConstants.STATUS_NORMAL);
appUser.setPassword(ENCODER.encode(appUser.getPassword()));
baseMapper.insert(appUser);
return null;
}
/**
* 导入用户(从Excel导入)
*
* @param excelVOList Excel数据列表
* @param bindingResult 数据校验结果
* @return 导入结果
*/
@Override
public R importUser(List<AppUserExcelVO> excelVOList, BindingResult bindingResult) {
// 获取通用校验失败的数据
List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
// 获取现有用户和角色列表,用于校验
List<AppUser> userList = this.list();
List<AppRole> roleList = appRoleService.list();
// 遍历Excel数据进行处理
for (AppUserExcelVO excel : excelVOList) {
Set<String> errorMsg = new HashSet<>();
// 校验用户名是否已存在
boolean exsitUserName = userList.stream()
.anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername()));
if (exsitUserName) {
errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_USER_USERNAME_EXISTING, excel.getUsername()));
}
// 校验角色名称是否合法
List<String> roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA);
List<AppRole> roleCollList = roleList.stream()
.filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name)))
.toList();
if (roleCollList.size() != roleNameList.size()) {
errorMsg.add(MsgUtils.getMessage(AppErrorCodes.APP_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList()));
}
// 数据合法时执行插入
if (CollUtil.isEmpty(errorMsg)) {
insertExcelUser(excel, roleCollList);
} else {
// 记录不合法数据的错误信息
errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
}
}
// 存在错误信息时返回失败
if (CollUtil.isNotEmpty(errorMessageList)) {
return R.failed(errorMessageList);
}
return R.ok(null, MsgUtils.getMessage(AppErrorCodes.APP_USER_IMPORT_SUCCEED));
}
/**
* 插入Excel导入的用户数据
*
* @param excel Excel用户数据
* @param roleCollList 角色列表
*/
private void insertExcelUser(AppUserExcelVO excel, List<AppRole> roleCollList) {
// 创建用户DTO并设置基本信息
AppUserDTO userDTO = new AppUserDTO();
userDTO.setUsername(excel.getUsername());
userDTO.setPhone(excel.getPhone());
userDTO.setNickname(excel.getNickname());
userDTO.setName(excel.getName());
userDTO.setEmail(excel.getEmail());
// 批量导入时初始密码设置为手机号
userDTO.setPassword(userDTO.getPhone());
// 获取角色ID列表
List<Long> roleIdList = roleCollList.stream().map(AppRole::getRoleId).toList();
userDTO.setRole(roleIdList);
// 保存用户
this.saveUser(userDTO);
}
}
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.app.mapper.AppContactsMapper">
<resultMap id="appContactsMap" type="com.pig4cloud.pigx.app.api.entity.AppContactsEntity">
<id property="id" column="id"/>
<result property="contactName" column="contact_name"/>
<result property="contactPhone" column="contact_phone"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
</mapper>
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2026, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.app.mapper.AppRoleMapper">
<resultMap id="appRoleMap" type="com.pig4cloud.pigx.app.api.entity.AppRole">
<id property="roleId" column="role_id"/>
<result property="roleName" column="role_name"/>
<result property="roleCode" column="role_code"/>
<result property="roleDesc" column="role_desc"/>
<result property="createBy" column="create_by"/>
<result property="updateBy" column="update_by"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
<select id="listRolesByUserId" resultType="com.pig4cloud.pigx.app.api.entity.AppRole">
SELECT app_role.role_id,
app_role.role_name,
app_role.role_code,
app_role.role_desc,
app_role.create_time,
app_role.update_time,
app_role.del_flag
FROM app_role,
app_user_role
WHERE app_role.role_id = app_user_role.role_id
AND app_role.del_flag = '0'
and app_user_role.user_id = #{userId}
</select>
</mapper>
@@ -9,6 +9,8 @@
<result property="selected" column="selected"/>
<result property="unselected" column="unselected"/>
<result property="link" column="link"/>
<result property="sortOrder" column="sort_order"/>
<result property="loginFlag" column="login_flag"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
@@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2026, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.app.mapper.AppUserMapper">
<resultMap id="appUserMap" type="com.pig4cloud.pigx.app.api.vo.AppUserVO">
<id property="userId" column="user_id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="phone" column="phone"/>
<result property="avatar" column="avatar"/>
<result property="nickname" column="nickname"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
<result property="createTime" column="create_time"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
<result property="lastModifiedTime" column="last_modified_time"/>
<collection property="roleList" ofType="com.pig4cloud.pigx.app.api.entity.AppRole"
select="com.pig4cloud.pigx.app.mapper.AppRoleMapper.listRolesByUserId" column="user_id">
</collection>
</resultMap>
<select id="getUserVosPage" resultMap="appUserMap">
select
u.user_id,
u.username,
u.password,
u.phone,
u.avatar,
u.create_time,
u.del_flag,
u.lock_flag,
u.tenant_id,
u.nickname,
u.name,
u.email
from app_user u
<where>
u.del_flag = '0'
<if test="query.username != null and query.username != ''">
<bind name="usernameLike" value="'%'+query.username+'%'"/>
AND u.username LIKE #{usernameLike}
</if>
</where>
</select>
<sql id="userRoleDeptSql">
u.user_id,
u.username,
u.password,
u.salt,
u.phone,
u.avatar,
u.wx_openid,
u.del_flag,
u.lock_flag,
u.tenant_id,
u.nickname,
u.name,
u.email,
u.create_by,
u.create_time ucreate_time,
u.update_time uupdate_time,
r.role_id
</sql>
<select id="getUserVoById" resultMap="appUserMap">
SELECT
<include refid="userRoleDeptSql"/>
FROM
app_user u
LEFT JOIN app_user_role urole ON urole.user_id = u.user_id
LEFT JOIN app_role r ON r.role_id = urole.role_id
WHERE
u.user_id = #{id}
</select>
</mapper>
@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright (c) 2018-2026, lengleng All rights reserved.
~
~ Redistribution and use in source and binary forms, with or without
~ modification, are permitted provided that the following conditions are met:
~
~ Redistributions of source code must retain the above copyright notice,
~ this list of conditions and the following disclaimer.
~ Redistributions in binary form must reproduce the above copyright
~ notice, this list of conditions and the following disclaimer in the
~ documentation and/or other materials provided with the distribution.
~ Neither the name of the pig4cloud.com developer nor the names of its
~ contributors may be used to endorse or promote products derived from
~ this software without specific prior written permission.
~ Author: lengleng (wangiegie@gmail.com)
~
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.pig4cloud.pigx.app.mapper.AppUserRoleMapper">
<resultMap id="appUserRoleMap" type="com.pig4cloud.pigx.app.api.entity.AppUserRole">
<id property="userId" column="user_id"/>
<result property="roleId" column="role_id"/>
</resultMap>
</mapper>