培训

组织机构、岗位、用户、角色配置

组织机构的变更都在 控制台->组织架构

服务端

提供一些常用的接口

组织人员机构接口

前端

提供单选的组件、提供选择组织架构选择器组件

单选-异步

组织机构选择器

菜单及权限配置

菜单配置

菜单配置在 控制台 ->应用->自定义菜单
菜单分为web、app菜单两种
菜单权限配置,菜单的权限配置现在是通过角色来配置的,角色关联人、关联菜单

权限配置

关联员工:角色关联员工
菜单权限:角色关联菜单
功能权限:角色关联套件定义的用户组
接口权限:使用注解 @PreAuthorize(“@pms.hasSuiteRoles(‘套件code:用户组:用户组权限’)”)

接口权限

配置用户组和业务权限

java代码
    @ApiModelProperty("需要权限")
    @GetMapping("/must/perm")
    @PreAuthorize("@pms.hasSuiteRoles('cn.flyrise.test.a:work:select')")
    public Reply<?> mustPerm() {

        return Reply.success("有权限");
    }
无权限返回信息
{
  "code": "999",
  "data": null,
  "time": 1657073219861,
  "msg": "服务异常,请联系管理员",
  "annex": {
    "applicationName": "pai-hello",
    "ExceptionClassName": "org.springframework.security.access.AccessDeniedException",
    "errorMsg": "不允许访问"
  },
  "success": false
}

消息通知

消息模版配置

消息模版是在开发者平台配置的
位置:套件->能力->消息服务
消息类型目前支持:站内消息(pc),app推送(极光),短信(阿里、华为、岭南大数据、漫道),邮件(smtp)
短信默认是阿里大鱼(比较麻烦需要审核)
消息支持动态参数配置 通过配置{param},发送参数传递param参数就能实现动态替换

添加消息后,编辑进入模版管理

设置自定义消息参数

发送代码测试


@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class McTest {

    private static final transient Logger logger = LoggerFactory.getLogger(McTest.class);

    private MockMvc mvc;
    @Autowired
    private WebApplicationContext applicationContext; // 注入WebApplicationContext

    @Before
    public void setUp() {
        this.mvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
    }


    @Resource
    private McTemplateService mcTemplateService;


    @Test
    @WithMockOAuth2User(username = "15918411916", password = "123456")
    public void sendMcTest() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        //开发者后台创建的消息编号
        String moban = "MP_10000256";
        String title = "柬媒评论:中国不应是唯一的新冠溯源目标";
        String content = "近日,,新冠病毒防范疫情被政治化。……";
        InnerUserBody innerUserBody = InnerUserBody.builder()
                .categoryCode(moban)
                .enterpriseId(loginUser.getEntId())
                .sendUserId(loginUser.getUserId())
                .sendUserName(loginUser.getStaffName())
                .receiveUserType("user")
                .receiveUserIds(new String[]{loginUser.getUserId()})
                .pageH5Id("H5Id")
                .param("title", title)
                .param("context", content)
                .build();
        Reply reply = mcTemplateService.sendInnerUser(innerUserBody);
        logger.info(JSONUtil.toJsonStr(reply));
    }


}

短信验证码

pai.sms.enable=false,默认值是true,false的时候关闭短信的发送
pai.sms.fixed-code,默认值是1234,使用verificationGeneratorHelper工具类获取随机的验证码
当enable为false的时候,验证码是当前设置的fixedCode
配置文件:application.yml or nacos->pai-xxx-xx.yaml

配置开关,默认值

pai:
  sms:
    enable: true
    fixed-code: 1234

获取验证码

    //注入工具类
    @Resource
    private VerificationGeneratorHelper verificationGeneratorHelper;

    //获取数字的验证码
    String code = verificationGeneratorHelper.getNumberCode();
    String code = verificationGeneratorHelper.getNumberCode(Integer);
    //获取字符的随机验证码
    String code = verificationGeneratorHelper.getCode();
    String code = verificationGeneratorHelper.getCode(Integer);

Demo

public interface ISmsService {


    /**
     * 发送短信验证码
     *
     * @param smsKey 模板key
     * @param phone  手机号
     * @return
     */
    boolean sendValidateCode(String smsKey, String phone);

    /**
     * 发送短信验证码
     *
     * @param smsKey 模板key
     * @param phone  手机号
     * @param map    参数
     * @return
     */
    boolean sendValidateCode(String smsKey, String phone, Map<String, String> map);

    /**
     * 发送短信验证码
     *
     * @param smsKey 模板key
     * @param phone  手机号
     * @param map    参数
     * @return
     */
    boolean sendSms(String smsKey, String phone, Map<String, String> map);


    /**
     * 校验验证码
     *
     * @param phone
     * @param code
     * @return
     */
    boolean validateCode(String smsKey, String phone, String code);

}
@Service
public class SmsServiceImpl implements ISmsService {

    private static final Logger log = LoggerFactory.getLogger(SmsServiceImpl.class);


    @Autowired
    private RedisService redisService;

    @Resource
    private McService mcService;

    @Resource
    private VerificationGeneratorHelper verificationGeneratorHelper;


    @Override
    public boolean sendSms(String smsKey, String phone, Map<String, String> map) {
        SmsOuterUser smsOuterUser = new SmsOuterUser();
        smsOuterUser.setParam(map);
        smsOuterUser.setTemplateCode(smsKey);
        smsOuterUser.setReceiveMobiles(new String[]{phone});
        smsOuterUser.setBusinessSystemId("console");
        smsOuterUser.setReceiveUserType("staff");
        FeignMessage msg = mcService.sendOuterUserSms(smsOuterUser);
        return true;
    }

    private FeignMessage sendSms(String templateCode, String phone, String code) {
        Map<String, String> map = new HashMap<>();
        map.put("code", code);
        SmsOuterUser smsOuterUser = new SmsOuterUser();
        smsOuterUser.setParam(map);
        smsOuterUser.setTemplateCode(templateCode);
        smsOuterUser.setReceiveMobiles(new String[]{phone});
        smsOuterUser.setBusinessSystemId("console");
        smsOuterUser.setReceiveUserType("staff");
        FeignMessage msg = mcService.sendOuterUserSms(smsOuterUser);

        return msg;
    }

    @Override
    public boolean sendValidateCode(String templateCode, String phone) {
        String cacheKey = CacheConstants.SMS + templateCode + CacheConstants.COLON + phone;
        //获取数字的验证码
        String code = verificationGeneratorHelper.getNumberCode();

        FeignMessage smsMsg = sendSms(templateCode, phone, code);
        if (!smsMsg.isOk()) {
            String msg = smsMsg.getMsg();
            String data = "";
            if (smsMsg.getData() != null) {
                data = smsMsg.getData().toString();
            }
            throw new ConsoleBizException(BusinessErrors.ERR_1014, msg, data);
        }

        log.info("发送短信 phone:{} code:{}", phone, code);
        int EXPIRATION = 5;
        redisService.setCacheObject(cacheKey, code, EXPIRATION, TimeUnit.MINUTES);
        return true;
    }


    @Override
    public boolean sendValidateCode(String templateCode, String phone, Map<String, String> map) {
        String cacheKey = CacheConstants.SMS + templateCode + CacheConstants.COLON + phone;
        //获取数字的验证码
        String code = verificationGeneratorHelper.getNumberCode();
        map.put("code", code);
        boolean sendValidateCode = sendSms(templateCode, phone, map);
        if (!sendValidateCode) {
            throw new ConsoleBizException("500", "服务异常,请稍后尝试");
        }
        log.info("发送短信 phone:{} code:{}", phone, code);
        int EXPIRATION = 5;
        redisService.setCacheObject(cacheKey, code, EXPIRATION, TimeUnit.MINUTES);
        return true;
    }

    @Override
    public boolean validateCode(String templateCode, String phone, String code) {
        String cacheKey = CacheConstants.SMS + templateCode + CacheConstants.COLON + phone;

        String redisRegisterCode = redisService.getCacheObject(cacheKey);
        if (StrUtil.isNotEmpty(redisRegisterCode)) {
            if (code.equals(redisRegisterCode)) {
                return true;
            }
        }
        return false;
    }
}

流程开发

开发文档

点击查看流程开发

表单如何自定义数据源

无,具体需求不明确

工单开发

如果无权限访问,请联系管理员

工单 - WEB开发

点击查看WEB开发

工单 - APP开发

点击查看APP开发

工单 - 服务端开发

点击查看服务端开发

中间件配置及开发

oss

后端使用
在nacos中添加配置

oss:
  appId: xiwan-sIOuajfe
  secretKey: rxkg3dll8eqsjvph 

pom.xml中引入包

        <dependency>
            <groupId>cn.flyrise</groupId>
            <artifactId>pai-common-oss</artifactId>
        </dependency>

新增一个测试类


import cn.flyrise.common.oss.OssClient;
import cn.flyrise.common.oss.domain.FileObject;
import cn.flyrise.pai.xwservicetest.Application;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import feign.Request;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.io.File;
import java.util.List;
import java.util.concurrent.TimeUnit;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class OssTest {

    @Resource
    private OssClient client;

    private String attachId;

    private String copyTargetAttachId;
    private String fileCode;

    @Test
    public void run() {
        saveOneFile();
        listAttach(attachId);
        appendFile();
        getAndWriteFile();
        deleteFileCode();
        copyAttach();
        deleteAttach();
    }

    /**
     * 保存一张图片
     */

    public void saveOneFile() {
        System.out.println("---保存一张图片---");
        // 上传者,仅用作记录,正常应该从token中获取用户信息
        String uploadBy = "西湾";
        File file = FileUtil.file("D:\\home\\翠亨\\所有权限角色.png");
        FileObject fileObject = client.save(file, uploadBy);
        System.out.println(fileObject);
        attachId = fileObject.getAttachId();
    }

    /**
     * 根据附件ID查询附件下的文件
     */

    public void listAttach(String attachId) {
        System.out.println(CharSequenceUtil.format("---查询附件{}下的文件---", attachId));
        List<FileObject> list = client.list(attachId);
        System.out.println(list);
    }

    public void copyAttach() {
        System.out.println("---复制文件---");
        copyTargetAttachId = client.copy(attachId, null);

        System.out.println("---查询复制后的附件下的文件---");
        listAttach(copyTargetAttachId);
    }

    /**
     * 往附件中追加文件
     */

    public void appendFile() {
        System.out.println("---往附件中追加一个文件---");
        String uploadBy = "西湾";
        File file = FileUtil.file("D:\\home\\翠亨\\角色关联员工.png");
        FileObject fileObject = client.save(file, attachId, uploadBy);
        System.out.println(fileObject);
        fileCode = fileObject.getFileCode();
        listAttach(attachId);
    }


    /**
     * 转存文件
     */

    public void getAndWriteFile() {
        System.out.println("---转存文件前--检查欲存储文件是否存在---");
        File file = FileUtil.file("D:\\home\\翠亨\\角色关联员工2.png");
        System.out.println(FileUtil.exist(file));
        //如果文件比较大,则可以指定请求连接时间,避免连接超时
        Request.Options options = new Request.Options(20L, TimeUnit.SECONDS, 20L, TimeUnit.SECONDS, true);
        //InputStream inputStream =  client.getInputStream(options, fileCode);
        byte[] bytes = client.get(options, fileCode);
        FileUtil.writeBytes(bytes, FileUtil.file(file));
        System.out.println("---转存文件后--检查欲存储文件是否存在---");
        System.out.println(FileUtil.exist(file));
    }


    public void deleteFileCode() {
        System.out.println("---删除一个文件---");
        boolean isDeleted = client.delete(attachId, fileCode);
        System.out.println(isDeleted);
        listAttach(attachId);
    }

    public void deleteAttach(){
        System.out.println("---删除附件---");
        client.delete(attachId);
        client.delete(copyTargetAttachId);

        listAttach(attachId);
        listAttach(copyTargetAttachId);
    }

}

控制台输出

{attachId='fca9d2780c834ee9909d15783b41ebd0', fileCode='1bcb0afd7a9c47f2b230efdc7c62cfbb', orgFileName='所有权限角色.png', suffix='.png', fileSize='9690', uploadUser='西湾', uploadTime='null', url='http://testpai.zschzhcs.com/oss-api/oss/v1/1bcb0afd7a9c47f2b230efdc7c62cfbb'}
---查询附件fca9d2780c834ee9909d15783b41ebd0下的文件---
[{attachId='fca9d2780c834ee9909d15783b41ebd0', fileCode='1bcb0afd7a9c47f2b230efdc7c62cfbb', orgFileName='所有权限角色.png', suffix='.png', fileSize='9690', uploadUser='西湾', uploadTime='Tue Jul 05 15:14:53 CST 2022', url='http://testpai.zschzhcs.com/oss-api/oss/v1/1bcb0afd7a9c47f2b230efdc7c62cfbb'}]
---往附件中追加一个文件---
{attachId='fca9d2780c834ee9909d15783b41ebd0', fileCode='a937f5be8fab4c809e4ef00a8823abc5', orgFileName='角色关联员工.png', suffix='.png', fileSize='16657', uploadUser='西湾', uploadTime='null', url='http://testpai.zschzhcs.com/oss-api/oss/v1/a937f5be8fab4c809e4ef00a8823abc5'}
---查询附件fca9d2780c834ee9909d15783b41ebd0下的文件---
[{attachId='fca9d2780c834ee9909d15783b41ebd0', fileCode='a937f5be8fab4c809e4ef00a8823abc5', orgFileName='角色关联员工.png', suffix='.png', fileSize='16657', uploadUser='西湾', uploadTime='Tue Jul 05 15:14:54 CST 2022', url='http://testpai.zschzhcs.com/oss-api/oss/v1/a937f5be8fab4c809e4ef00a8823abc5'}, {attachId='fca9d2780c834ee9909d15783b41ebd0', fileCode='1bcb0afd7a9c47f2b230efdc7c62cfbb', orgFileName='所有权限角色.png', suffix='.png', fileSize='9690', uploadUser='西湾', uploadTime='Tue Jul 05 15:14:53 CST 2022', url='http://testpai.zschzhcs.com/oss-api/oss/v1/1bcb0afd7a9c47f2b230efdc7c62cfbb'}]
---转存文件前--检查欲存储文件是否存在---
false
---转存文件后--检查欲存储文件是否存在---
true
---删除一个文件---
15:14:53.806 [PollingServerListUpdater-0] INFO  c.n.c.ChainedDynamicProperty - [checkAndFlip,115] - Flipping property: pai-oss.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
true
---查询附件fca9d2780c834ee9909d15783b41ebd0下的文件---
[{attachId='fca9d2780c834ee9909d15783b41ebd0', fileCode='1bcb0afd7a9c47f2b230efdc7c62cfbb', orgFileName='所有权限角色.png', suffix='.png', fileSize='9690', uploadUser='西湾', uploadTime='Tue Jul 05 15:14:53 CST 2022', url='http://testpai.zschzhcs.com/oss-api/oss/v1/1bcb0afd7a9c47f2b230efdc7c62cfbb'}]
---复制文件---
---查询复制后的附件下的文件---
---查询附件06809202d7c247fcb1225789f5def762下的文件---
[{attachId='06809202d7c247fcb1225789f5def762', fileCode='1bcb0afd7a9c47f2b230efdc7c62cfbb', orgFileName='所有权限角色.png', suffix='.png', fileSize='9690', uploadUser='西湾', uploadTime='Tue Jul 05 15:14:53 CST 2022', url='http://testpai.zschzhcs.com/oss-api/oss/v1/1bcb0afd7a9c47f2b230efdc7c62cfbb'}]
---删除附件---
---查询附件fca9d2780c834ee9909d15783b41ebd0下的文件---
[]
---查询附件06809202d7c247fcb1225789f5def762下的文件---
[]

前端配置

找到nuxt.config.js

module.exports = {
    isOss: true
}

就可以使用前端组件了

注意

前端上传完成之后,后端记得确认保存。否则会当成临时文件,被定期清除

client.complete(attachId);

redis

后端pom.xml引入包

        <dependency>
            <groupId>cn.flyrise</groupId>
            <artifactId>pai-common-cache</artifactId>
        </dependency>

新增测试类


import cn.flyrise.common.redis.service.RedisService;
import cn.flyrise.pai.xwservicetest.Application;
import cn.hutool.core.thread.ThreadUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RedisTest {

    @Resource
    private RedisService service;

    @Test
    public void test() {
        //设定5秒过期
        //若不指定过期时间则一直存在,注意按需选择
        service.setCacheObject("xw:test", "1000", 5, TimeUnit.SECONDS);
        System.out.println((String) service.getCacheObject("xw:test"));
        //休眠8秒
        ThreadUtil.sleep(8000);
        //查看结果
        System.out.println((String) service.getCacheObject("xw:test"));
    }

}

控制台输出

1000
null

nacos

请求地址http://testpai.zschzhcs.com/nacos
账号:nacos
密码:@Nacos

注意:个性化的配置不要放到共享配置。

工程配置

spring:
  cloud:
    nacos:
      discovery:
        namespace: 5a1495ca-cdce-4c82-8b38-ddce5ae3c8c9
        server-addr: http://10.21.6.204:32018
        #Nacos 服务开启认证时需要配置
        #username: 
        #password: 
        #context-path: nacos
      config:
        namespace: ${spring.cloud.nacos.discovery.namespace}
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yaml

消息订阅MQ

点击查看消息订阅开发

物业系统功能如何聚合

使用中台框架开发的

添加菜单

外部应用

通过单点登录

数据库配置、安装、资源分配

文档更新时间: 2024-08-04 11:31   作者:姚连洲