package com.iplatform.base;
|
|
import com.iplatform.base.cache.DictCacheProvider;
|
import com.iplatform.base.di.PlatformDataImportEngine;
|
import com.iplatform.base.di.TemplateInfo;
|
import com.iplatform.base.service.DeptServiceImpl;
|
import com.iplatform.base.service.LogServiceImpl;
|
import com.iplatform.base.service.UserServiceImpl;
|
import com.iplatform.base.support.strategy.LoginStrategyManager;
|
import com.iplatform.base.util.MenuUtils;
|
import com.iplatform.core.BeanContextAware;
|
import com.iplatform.model.po.S_dept;
|
import com.iplatform.model.po.S_dict_data;
|
import com.iplatform.model.po.S_oper_log;
|
import com.iplatform.model.po.S_user_core;
|
import com.iplatform.model.po.S_user_login;
|
import com.walker.cache.CacheProvider;
|
import com.walker.infrastructure.utils.DateUtils;
|
import com.walker.infrastructure.utils.FileCopyUtils;
|
import com.walker.infrastructure.utils.NumberGenerator;
|
import com.walker.infrastructure.utils.PhoneNumberUtils;
|
import com.walker.infrastructure.utils.StringUtils;
|
import com.walker.push.PushManager;
|
import com.walker.web.CaptchaProvider;
|
import com.walker.web.CaptchaResult;
|
import com.walker.web.OrgType;
|
import com.walker.web.ResponseCode;
|
import com.walker.web.UserOnlineProvider;
|
import com.walker.web.UserPrincipal;
|
import com.walker.web.UserType;
|
import com.walker.web.WebAgentService;
|
import com.walker.web.WebUserAgent;
|
import com.walker.web.log.BusinessType;
|
import com.walker.web.log.OperateUser;
|
import com.walker.web.util.IdUtils;
|
import com.walker.web.util.ServletUtils;
|
|
import javax.servlet.http.HttpServletRequest;
|
import java.io.File;
|
import java.io.IOException;
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.TimerTask;
|
|
/**
|
* 系统控制器对象,业务所有 <code>Controller</code> 对象,都要继承该对象。
|
* @author 时克英
|
* @date 2022-12-01
|
*/
|
public abstract class SystemController extends AbstractFileOperateSpiController{
|
|
/**
|
* 更新给定用户登录缓存中的角色集合内容,roleIds。
|
* <pre>
|
* 1) 当用户变更角色后,可以直接刷新界面(重新请求用户信息)获取新的对应菜单权限。
|
* </pre>
|
* @param userId 用户ID
|
* @author 时克英
|
* @date 2023-12-28
|
*/
|
protected void updateLoginUserRoleListInCache(Long userId){
|
S_user_core userCore = this.getUser(userId);
|
if(userCore == null){
|
throw new PlatformRuntimeException("缓存中未查找到用户,这不正常,userId = " + userId, null);
|
}
|
S_user_login userLogin = this.getLoginStrategyManager().getUserLogin(userCore.getUser_name());
|
if(userLogin == null){
|
if(this.logger.isDebugEnabled()){
|
this.logger.debug("用户:{}不存在登录缓存,无需更新角色(roleId)缓存,userName={}", userId, userCore.getUser_name());
|
}
|
return;
|
}
|
|
UserPrincipal<?> existUserPrincipal = BeanContextAware.getBeanByType(UserOnlineProvider.class).getUserPrincipal(userLogin.getUuid());
|
List<String> existRoleIdList = existUserPrincipal.getRoleIdList();
|
|
List<String> roleIdList = this.getUserService().queryUserRoleIdList(userId);
|
if(this.logger.isDebugEnabled()){
|
this.logger.debug("给定用户:{},包含的角色:{}", userId, roleIdList);
|
}
|
if(StringUtils.isEmptyList(roleIdList)){
|
roleIdList = new ArrayList<>(4);
|
}
|
// 把原来用户已有的系统级权限角色重新写入,否则用户会重新让登录
|
if(existRoleIdList != null){
|
for(String securityRoleName : existRoleIdList){
|
if(securityRoleName.equals(SecurityConstants.ROLE_USER)
|
|| securityRoleName.equals(SecurityConstants.ROLE_ADMIN)
|
|| securityRoleName.equals(SecurityConstants.ROLE_SUPER_ADMIN)
|
|| securityRoleName.equals(SecurityConstants.ROLE_MERCHANT)){
|
roleIdList.add(securityRoleName);
|
}
|
}
|
}
|
|
DefaultUserPrincipal userPrincipal = new DefaultUserPrincipal(userCore);
|
userPrincipal.setRoleIdList(roleIdList);
|
// 更新缓存的登录信息
|
BeanContextAware.getBeanByType(UserOnlineProvider.class).cacheUserPrincipal(userLogin.getUuid(), userPrincipal, VariableConstants.DEFAULT_TOKEN_EXPIRED_MINUTES);
|
}
|
|
protected UserLoginCache getUserLoginCache(){
|
return BeanContextAware.getBeanByType(UserLoginCache.class);
|
}
|
|
/**
|
* 获得通知提醒模板配置缓存。
|
* @return
|
* @date 2023-08-25
|
*/
|
protected NotificationTemplateCache getNotificationTemplateCache(){
|
return BeanContextAware.getBeanByType(NotificationTemplateCache.class);
|
}
|
|
/**
|
* 写入成功操作日志
|
* @param userName 当前用户名,可以是登录ID或名字,可选
|
* @param operateUser 操作用户类型
|
* @param businessType 业务类型
|
* @param title 标题描述,可选
|
* @param input 输入参数,可选
|
* @param output 输出参数,可选
|
* @date 2023-08-13
|
*/
|
protected void systemLogSuccess(String userName, OperateUser operateUser, BusinessType businessType
|
, String title, String input, String output){
|
S_oper_log s_oper_log = this.acquireSystemLog(userName, operateUser, businessType, title, input, output);
|
AsyncManager.me().execute(new TimerTask() {
|
@Override
|
public void run() {
|
BeanContextAware.getBeanByType(LogServiceImpl.class).execInsertOperateLog(s_oper_log);
|
}
|
});
|
}
|
|
/**
|
* 写入错误日志
|
* @param userName 当前用户名,可以是登录ID或名字,可选
|
* @param operateUser 操作用户类型,可选
|
* @param businessType 业务类型,可选
|
* @param title 标题描述,可选
|
* @param input 输入参数,可选
|
* @param error 错误内容,必填
|
* @date 2023-08-13
|
*/
|
protected void systemLogError(String userName, OperateUser operateUser, BusinessType businessType, String title, String input, String error){
|
if(StringUtils.isEmpty(error)){
|
logger.warn("调用写入错误日志,但未提供错误内容,日志将不会保存:systemLogError --> error is null!");
|
return;
|
}
|
S_oper_log s_oper_log = this.acquireSystemLog(userName, operateUser, businessType, title, input, null);
|
s_oper_log.setStatus(ResponseCode.ERROR.getCode());
|
s_oper_log.setError_msg(StringUtils.substring(error, 0, Constants.LOG_ERROR_MAX_SIZE));
|
AsyncManager.me().execute(new TimerTask() {
|
@Override
|
public void run() {
|
BeanContextAware.getBeanByType(LogServiceImpl.class).execInsertOperateLog(s_oper_log);
|
}
|
});
|
}
|
|
private S_oper_log acquireSystemLog(String userName, OperateUser operateUser, BusinessType businessType
|
, String title, String input, String output){
|
if(operateUser == null){
|
operateUser = OperateUser.Manage;
|
}
|
if(businessType == null){
|
businessType = BusinessType.Other;
|
}
|
WebUserAgent agent = this.getCurrentWebUserAgent();
|
S_oper_log s_oper_log = new S_oper_log();
|
s_oper_log.setOper_id(NumberGenerator.getLongSequenceNumber());
|
s_oper_log.setOper_time(DateUtils.getDateTimeNumber());
|
s_oper_log.setStatus(ResponseCode.SUCCESS.getCode());
|
s_oper_log.setOper_name(userName);
|
s_oper_log.setOperate_user(operateUser.getIndex());
|
s_oper_log.setBusiness_type(businessType.getIndex());
|
s_oper_log.setOper_ip(agent.getIp());
|
s_oper_log.setOper_location(agent.getLocation());
|
s_oper_log.setRequest_method(agent.getMethod());
|
s_oper_log.setOper_url(agent.getUrl());
|
s_oper_log.setTitle(title);
|
if(StringUtils.isNotEmpty(input)){
|
s_oper_log.setOper_param(input);
|
}
|
if(StringUtils.isNotEmpty(output)){
|
s_oper_log.setJson_result(output);
|
}
|
return s_oper_log;
|
}
|
|
/**
|
* 返回登录策略管理器对象
|
* @return
|
* @date 2023-08-05
|
*/
|
protected LoginStrategyManager getLoginStrategyManager(){
|
return BeanContextAware.getBeanByType(LoginStrategyManager.class);
|
}
|
|
/**
|
* 返回当前web浏览器代理对象。
|
* @return
|
* @date 2023-07-25
|
*/
|
protected WebUserAgent getCurrentWebUserAgent(){
|
HttpServletRequest request = this.getRequest();
|
return this.getWebAgentService().getWebUserAgent(request.getHeader("User-Agent"), request);
|
}
|
|
protected WebAgentService getWebAgentService(){
|
return BeanContextAware.getBeanByType(WebAgentService.class);
|
}
|
|
/**
|
* 判断短信验证码是否正确。
|
* @param code 验证码
|
* @param uuid 请求验证码标识
|
* @return
|
* @date 2023-08-07
|
*/
|
protected boolean validateSmsCode(String code, String uuid){
|
CaptchaResult captchaResult = new CaptchaResult();
|
captchaResult.setCode(code);
|
captchaResult.setUuid(uuid);
|
return this.smsCaptchaProvider.validateCaptcha(captchaResult);
|
}
|
|
/**
|
* 发送短信验证码。
|
* @param phoneNumber 手机号
|
* @return 返回前端需要的uuid,提交登录使用
|
* @throws PlatformRuntimeException
|
* @date 2023-06-28
|
*/
|
protected Map<String, Object> sendSmsCodeValidation(String phoneNumber){
|
if(!PhoneNumberUtils.isCellPhoneNumber(phoneNumber)){
|
throw new PlatformRuntimeException("手机号码格式错误:" + phoneNumber);
|
}
|
String uuid = IdUtils.simpleUUID();
|
Map<String, Object> data = new HashMap<>(4);
|
data.put("uuid", uuid);
|
String verifyKey = Constants.CAPTCHA_CODE_PREFIX + uuid;
|
CaptchaResult captchaResult = this.smsCaptchaProvider.generateCaptcha(phoneNumber);
|
if(captchaResult == null){
|
// return ResponseValue.error("短信验证码生成错误, null");
|
throw new PlatformRuntimeException("短信验证码生成错误, null");
|
}
|
// 写入验证码 key 和 code 到缓存中
|
this.captchaCacheProvider.putCacheData(verifyKey, captchaResult.getCode(), 120);
|
if(this.logger.isDebugEnabled()){
|
this.logger.debug("生成短信验证码:{}, uuid:{}", captchaResult.getCode(), uuid);
|
}
|
data.put("message", "短信验证码已发送");
|
return data;
|
}
|
|
/**
|
* 返回用户归属值,如果是平台为'-1',如果为租户(商户)则为定义的商户ID。
|
* <pre>
|
* 注意要点:
|
* 1)因为系统框架中用户是长整形(Long),但商户中是(int)因此在实际添加商户数据时,需要按照序列来计算,这样只需要整形即可。
|
* </pre>
|
* @return
|
* @date 2023-06-05
|
*/
|
protected long getOwner(){
|
S_user_core user = this.getCurrentUser();
|
int userType = user.getUser_type();
|
if(userType == UserType.TYPE_SUPER){
|
return Constants.OWNER_PLATFORM;
|
}
|
// 其他人需要根据机构中"menu_type"字段区分是平台机构,还是业务独立机构
|
S_dept dept = this.getDept(user.getOrg_id());
|
if(dept.getMenu_type().intValue() == MenuUtils.MENU_SCOPE_PLATFORM){
|
return Constants.OWNER_PLATFORM;
|
}
|
// 机构ID就是业务创建的独立编号(如:商户id),这里商户是int,但系统机构中仍然使用bigint
|
return dept.getId();
|
}
|
|
/**
|
* 返回当前用户所在顶级单位,使用的菜单范围:平台0,商户(顶级独立单位菜单范围)4,后续可能会有其他值。
|
* @return
|
* @date 2023-06-01
|
*/
|
protected int getCurrentOrgMenuScope(){
|
long orgId = this.getCurrentUser().getOrg_id();
|
if(orgId == Constants.SUPERVISOR_ID){
|
// 超级管理员,返回平台菜单范围
|
return MenuUtils.MENU_SCOPE_PLATFORM;
|
}
|
return this.getDeptCacheProvider().getDept(orgId).getMenu_type();
|
}
|
|
/**
|
* 根据字典数据code,返回字典项名称。
|
* @param dictCode 字典项编码(主键)
|
* @return
|
* @date 2023-03-26
|
*/
|
protected String getDictName(long dictCode){
|
S_dict_data dict_data = this.getDictCacheProvider().getCacheData(String.valueOf(dictCode));
|
if(dict_data == null){
|
throw new IllegalStateException("缓存中未找到字典名称,dict_code = " + dictCode);
|
}
|
return dict_data.getDict_label();
|
}
|
|
/**
|
* 返回给定机构的名字。
|
* @param deptId 机构ID
|
* @return
|
* @date 2023-03-23
|
*/
|
protected String getDeptName(long deptId){
|
S_dept dept = this.getDept(deptId);
|
return dept == null ? "None":dept.getDept_name();
|
}
|
|
/**
|
* 返回给定id的部门对象
|
* @param deptId 部门ID
|
* @return
|
* @author 时克英
|
* @date 2023-03-23
|
*/
|
protected S_dept getDept(long deptId){
|
return this.getDeptCacheProvider().getDept(deptId);
|
}
|
|
/**
|
* 从缓存中返回给定的用户
|
* @param userId 用户id
|
* @return
|
* @date 2023-03-22
|
*/
|
public S_user_core getUser(long userId){
|
return this.getUserCacheProvider().getUser(userId);
|
}
|
|
/**
|
* 下载数据导入模板,该模板由系统自动生成。<br>
|
* 业务需要提供数据库对应的'表名称',如:s_user_core
|
* @param tableName 表名
|
* @date 2023-03-18
|
*/
|
protected void downloadLocalImportTemplate(String tableName){
|
if(StringUtils.isEmpty(tableName)){
|
throw new IllegalArgumentException("请提供要导入数据的'表名称'!");
|
}
|
// 本地文件存储根路径,如: d:/temp/
|
String templatePath = this.acquireFileOperateSpi().getFileRootConfig();
|
// 生成的模板文件名称,如:demo.xlsx
|
String templateFileName = Constants.TEMPLATE_IMPORT_PREFIX + tableName + "_" + DateUtils.getDateTimeSecondForShow() + ".xlsx";
|
TemplateInfo templateInfo = new TemplateInfo();
|
// 2023-05-07 设置模板生成具体路径。
|
templateInfo.setTemplatePath(templatePath + templateFileName);
|
templateInfo.setTableName(tableName);
|
|
logger.debug("templateInfo = {}", templateInfo);
|
|
File templateFile = this.getDataImportEngine().generateTemplate(templateInfo);
|
try {
|
this.downloadSimpleFile(FileCopyUtils.copyToByteArray(templateFile), templateFileName);
|
} catch (IOException e) {
|
logger.error("下载模板错误:" + e.getMessage() + ", tableName=" + tableName, e);
|
ServletUtils.renderString(getResponse(), "下载模板错误:" + e.getMessage() + ", tableName=" + tableName);
|
}
|
}
|
|
/**
|
* 返回数据字典缓存对象。<p></p>
|
* 注意:这种写法后续要改掉,统一由平台自动注入,无需单独设置。
|
* @return
|
* @date 2023-03-10
|
*/
|
protected DictCacheProvider getDictCacheProvider(){
|
// if(this.dictCacheProvider == null){
|
// this.dictCacheProvider = BeanContextAware.getBeanByType(DictCacheProvider.class);
|
// }
|
return this.dictCacheProvider;
|
}
|
|
/**
|
* 返回数据导入引擎实现对象,用来完成 Excel 导入功能。
|
* @return
|
* @date 2023-02-07
|
*/
|
protected PlatformDataImportEngine getDataImportEngine(){
|
// return BeanContextAware.getBeanByType(PlatformDataImportEngine.class);
|
return this.platformDataImportEngine;
|
}
|
|
public UserCacheProvider getUserCacheProvider() {
|
// if(this.userCacheProvider == null){
|
// this.userCacheProvider = BeanContextAware.getBeanByType(UserCacheProvider.class);
|
// }
|
return userCacheProvider;
|
}
|
|
public DeptCacheProvider getDeptCacheProvider() {
|
// if(this.deptCacheProvider == null){
|
// this.deptCacheProvider = BeanContextAware.getBeanByType(DeptCacheProvider.class);
|
// }
|
return deptCacheProvider;
|
}
|
|
/**
|
* 返回给定用户的顶级机构ID。
|
* @param userId
|
* @return
|
* @date 2022-12-15
|
*/
|
protected long getUserRootOrgId(long userId){
|
this.checkUserCacheProvider();
|
S_user_core user_core = this.userCacheProvider.getUser(userId);
|
if(user_core == null){
|
throw new IllegalStateException("缓存中未找到用户: " + userId);
|
}
|
return user_core.getOrg_id();
|
}
|
|
/**
|
* 根据机构(部门等非顶级ID)返回顶级机构ID。
|
* @param deptId
|
* @return
|
* @date 2022-12-12
|
*/
|
protected long getRootOrgIdByDept(long deptId){
|
if(this.deptCacheProvider == null){
|
throw new IllegalArgumentException("请先设置controller对象: deptCacheProvider");
|
}
|
S_dept dept = this.deptCacheProvider.getDept(deptId);
|
if(dept == null){
|
throw new IllegalStateException("缓存中未找到机构: " + deptId);
|
}
|
return dept.getOrg_id();
|
}
|
|
/**
|
* 返回当前登录用户可选择的根机构列表,即:第一级机构,通常是集团公司等。<p></p>
|
* 这些也称为多租户,系统提供多单位(独立)维护,不同独立单位由各自管理员自行维护。
|
* <pre>
|
* 1.超级管理员可以看到所有独立机构列表
|
* 2.单位用户只能看到本单位(一个机构)
|
* </pre>
|
* @return
|
* @date 2022-12-01
|
*/
|
protected List<S_dept> getOrgListScope(){
|
DeptServiceImpl deptService = BeanContextAware.getBeanByType(DeptServiceImpl.class);
|
S_user_core currentUser = this.getCurrentUser();
|
List<S_dept> list = null;
|
if(currentUser.getUser_type().intValue() == UserType.TYPE_SUPER){
|
list = deptService.queryRootOrgList(0);
|
} else {
|
list = deptService.queryRootOrgList(currentUser.getOrg_id().longValue());
|
}
|
if(StringUtils.isEmptyList(list)){
|
// 不存在任何机构(数据库没有记录),需要创建默认机构
|
list = new ArrayList<>(2);
|
list.add(this.createDefaultOrg());
|
}
|
return list;
|
}
|
|
private void checkUserCacheProvider(){
|
if(this.userCacheProvider == null){
|
throw new IllegalStateException("UserCacheProvider 必须先设置到控制器中。");
|
}
|
}
|
|
private S_dept createDefaultOrg(){
|
S_dept root = new S_dept();
|
root.setId(0L);
|
root.setParent_id(0L);
|
root.setOrg_type(OrgType.TYPE_ORG);
|
root.setOrder_num(1);
|
root.setDept_name(Constants.DEFAULT_ORG_NAME);
|
return root;
|
}
|
|
public void setDeptCacheProvider(DeptCacheProvider deptCacheProvider) {
|
this.deptCacheProvider = deptCacheProvider;
|
}
|
|
public void setUserCacheProvider(UserCacheProvider userCacheProvider) {
|
this.userCacheProvider = userCacheProvider;
|
}
|
|
public void setPlatformDataImportEngine(PlatformDataImportEngine platformDataImportEngine) {
|
this.platformDataImportEngine = platformDataImportEngine;
|
}
|
|
public void setDictCacheProvider(DictCacheProvider dictCacheProvider) {
|
this.dictCacheProvider = dictCacheProvider;
|
}
|
|
public void setPushManager(PushManager pushManager) {
|
this.pushManager = pushManager;
|
}
|
|
public void setSmsCaptchaProvider(CaptchaProvider<CaptchaResult> smsCaptchaProvider) {
|
this.smsCaptchaProvider = smsCaptchaProvider;
|
}
|
|
public void setCaptchaCacheProvider(CacheProvider<String> captchaCacheProvider) {
|
this.captchaCacheProvider = captchaCacheProvider;
|
}
|
|
public CaptchaProvider<CaptchaResult> getSmsCaptchaProvider() {
|
return smsCaptchaProvider;
|
}
|
|
public CacheProvider<String> getCaptchaCacheProvider() {
|
return captchaCacheProvider;
|
}
|
|
protected UserServiceImpl getUserService(){
|
return BeanContextAware.getBeanByType(UserServiceImpl.class);
|
}
|
|
private CaptchaProvider<CaptchaResult> smsCaptchaProvider;
|
private CacheProvider<String> captchaCacheProvider;
|
|
/**
|
* 返回推送管理器对象,只有在特殊情况下需要业务直接调用该对象。
|
* @return
|
* @date 2023-04-25
|
*/
|
protected PushManager getPushManager() {
|
return pushManager;
|
}
|
|
private PushManager pushManager;
|
private DeptCacheProvider deptCacheProvider;
|
private UserCacheProvider userCacheProvider;
|
private PlatformDataImportEngine platformDataImportEngine;
|
private DictCacheProvider dictCacheProvider;
|
|
@Override
|
public void afterPropertiesSet() throws Exception {}
|
}
|