shikeyin
2024-01-11 65da8373531677b1c37a98f53eaa30c892f35e5a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
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 {}
}