实训过程总结

源代码见 https://github.com/Eason815/sie-mes

学习笔记

Day01

2024.10.19

IIDP工业数字平台介绍

工业软件的定义

应用于工业领域
以提高工业企业研发、生产、管理水平和工业装备性能的应用软件

工业软件的分类

1.png

MES系统

<制造执行系统>

生产控制承上启下

工业软件架构演变

2.png

模型驱动设计

模型驱动架构(Model Driven Architecture, MDA) ,以模型为中心,使用元数据定义一个全新的模型体系,让所有模型皆可扩展,让应用能快速响应业务变化。

3.png

IIDP开发环境搭建

  • Java 1.8
  • MySQL 8.0+
  • IDEA
  • Nginx
  1. 新建空数据库
  2. 启动后端服务
  3. 启动前端服务

技术架构

引擎加载逻辑

4.png

架构

5.png

需求说明

目标

  1. 工厂可以制作很多产品。产品由多种物料加工组装而成。
  2. 每个产品会按着严格的工艺路线进行加工。每一个步骤都是一个工序。
  3. 每个产品都会有一个产品 BOM(Bill of Material),代表加工该产品所需物料。
  4. 每个工序也会有一个 BOM,代表该工序加工时的所需物料。
  5. 每生产一个批次的产品,都会有一个工单。
  6. 不同工单可能使用不同的工艺路线。

模型设计

6.png

模型

模型物料定义

com.sie.app.demo/model/Material.java

1
2
3
4
5
6
7
8
@Model(name = "material",
tableName = "ems-material",
displayName = "物料",
description = "物料信息",
isAutoLog = Bool.True
)
public class Material extends BaseModel<Material>{
}

模型会使用 @Model 注解标识,并且继承 BaseModel 类型。以下是 @Model 注解的属性。上图定义了一个名为 material 的模型,并指定表名为 ems_material。

当前会先使用缓存视图

开发者中心-模型管理

选择对应模型 [生成视图]

开发者中心-视图管理

复制视图preview

``com.sie.app.demo/view/ems-material.json`

粘贴保存视图文件

``com.sie.app.demo/app.json`

更新视图列表

下次启动会使用[ems-material.json]视图


Day02

2024.10.20

测试基本类型

@Property 注解

使用 @Property 注解可以给模型增加属性

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
@Model(name = "test_test1",displayName = "测试基本类型",type=Model.ModelType.Buss)
public class TestTest1 extends BaseModel<TestTest1> {
    @Property(displayName = "测试字符串")
    private String testStr;
   
    @Property(displayName = "测试整形类型")
    private int testIntType;

    @Property(displayName = "测试整形包装类型")
    private Integer testIntegerType;

    @Property(displayName = "测试长整型类型")
    private Long testLongType;

    @Property(displayName = "测试双精度类型")
    private Float testFloatType;

    @Property(displayName = "测试浮点类型")
    private Double testDoubleType;

    @Property(displayName = "测试日期类型")
    private Date testDateTpe;

    @Property(displayName = "测试日期时间类型",dataType = DataType.DATE_TIME)
    private Date testDateTimeType;

    @Property(displayName = "测试数字计算类型",dataType = DataType.BIG_DECIMAL)
    private BigDecimal testBigDecimalType;
}

开发者中心-模型管理 [生成视图]
开发者中心-视图管理 粘贴视图
([重置种子数据] 重新获取)

menus.json

新增二级目录

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
{
  "menus": {
    "demo_root_menu": {
      "name": "demo_root_menu",
      "display_name": "物料管理系统",
      "active": true,
      "sequence": 1
    },
    "material_menu": {
      "name": "material_menu",
      "display_name": "物料管理",
      "model": "material",
      "view": "material_grid,material_search,material_form",
      "sequence": 1,
      "active": true,
      "parent_ids": {
        "@ref": "demo_root_menu"
      }
    },
    "product_menu": {
      "name": "product_menu",
      "display_name": "产品管理",
      "model": "product",
      "view": "product_grid,product_search,product_form",
      "sequence": 2,
      "active": true,
      "parent_ids": {
        "@ref": "demo_root_menu"
      }
    }
}

测试校验数据

@Validate 注解

@Validate 注解提供了常用的字段属性校验。用于新增、编辑模型的时候进行数据合法性校验。

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
@Model(name = "test_test2",displayName = "测试校验框架",type=Model.ModelType.Buss)
public class TestTest2 extends BaseModel<TestTest2> {
    @Property(columnName = "phone",displayName = "手机号码")
    @Validate.Phone(message = "手机号格式不正确")
    private String phone;

    @Property(columnName = "email",displayName = "邮箱")
    @Validate.Email(message = "邮箱格式不正确")
    private String email;

    @Validate.NotBlank(message = "年龄不能为空")
    @Validate.Max(value = 110,message = "年龄不能大于110岁")
    @Validate.Min(value = 18,message = "年龄不能小于18岁")
    @Property(columnName = "age",displayName = "年龄")
    private Integer age;

    @Validate.NotBlank(message = "总数不能为空")
    @Validate.Max(value = 1000,message = "总数不能大于1000")
    @Property(columnName = "totalNum",displayName = "总数")
    private Double totalNum;

    @Validate.Size(min = 10,max = 100)
    @Property(displayName = "学生名字",length = 10)
    private String stuName;

    @Validate.Unique(properties ={"stuName","stuCode"},message = "学生名字和学号必须唯一")
    @Property(displayName = "学号")
    private String stuCode;
}

打包省略正则表达式过程

下拉值选项

@Selection 注解

  @Selection 注解用于定义下拉取值的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Model(name = "test_test_3",displayName = "测试下拉框和字典")
public class TestTest3 extends BaseModel<TestTest3> {
    @Property(displayName = "1、单选常量",defaultValue = "1",widget = "select",length = 256)
    @Selection(values = {@Option(label = "状态1",value = "1"),
            @Option(label = "状态2",value = "2"),
            @Option(label = "状态3",value = "3")})
    private String status;

    @Property(displayName = "2、多选常量",defaultValue = "1",widget = "select",multiple = true,length = 256)
    @Selection(values = {@Option(label = "状态1",value = "1"),
            @Option(label = "状态2",value = "2"),
            @Option(label = "状态3",value = "3")})
    private String muiltStatus;

    @Property(displayName = "3-字典-状态")
    @Dict(typeCode = "validFlag")
    private String validFlag;

    @Property(displayName = "4-多选字典-状态")
    @Dict(typeCode = "validFlags2",multiple = true)
    private String validFlags2;
}

数据字典

@Dict 注解

  @Dict 注解用于声明使用数据字典

数据字典使用

租户端-平台主数据-数据字典

编码与typeCode对应,即[status]

Day03

2024.10.25

应用

安装应用

复制 sie-snest-mes 软件包

sie-iidp-apps/pom.xml

1
2
3
4
5
<modules>
<module>sie-snest-demo</module>
<module>sie-snest-mes</module>

</modules>

sie-snest-mes/pom.xml

1
<artifactId>sie-snest-mes</artifactId>

clean install 打jar包
应用市场 上传jar包
应用市场 [上架]
应用市场 [安装]

若 应用市场 删除了 <应用>

数据库 删除 表:

meta_app
meta_app_store

更新应用

  • 正确更新应用的方式
  1. 修改源代码
  2. 打新的jar包clean install (//重新调试?)
  3. 应用市场 上传新的jar包
  4. 应用市场 [上架]
  5. 已安装应用 [更新]
  6. 切换用户

模型 ER 关系

1 .@ManyToOne 多对一

  例如产品BOM 与产品模型形成多对一关系
  @ManyToOne 需要与 @JoinColumn 一起使用

2. @OneToMany 一对多

  例如产品与产品 BOM模型形成一对多关系

3. @ManyToMany 多对多

  例如用户与角色,形成多对多 ER 关系。@ManyToMany 需要与 @JoinTable 结合使用。@JoinTable 用于声明中间表的表明及关联字段名

com.sie.app.demo/model/TestTest4.java

1
2
3
4
5
6
7
8
9
10
@Model(name = "test_test_4", displayName = "测试ER模型4")
public class TestTest4 extends BaseModel<TestTest4>{
@Property(displayName = "名称")
private String name;
@Property(displayName = "编码")
private String code;

@OnetoMany
private List<TestTest5> testTest5List;
}

com.sie.app.demo/model/TestTest5.java

1
2
3
4
5
6
7
8
9
10
11
12
@Model(name = "test_test_5", displayName = "测试ER模型5")
public class TestTest5 extends BaseModel<TestTest5> {
    @Property(displayName = "名称")
    private String name;

    @Property(displayName = "编码")
    private String code;

    @ManyToOne(targetModel = "test_test_4", displayName = "测试ER模型4")
    @JoinColumn(name = "test_test_4_id")
    private TestTest4 testTest4;
}

com.sie.app.demo/model/TestTest6.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Model(name = "test_test_6", displayName = "测试ER模型6")
public class TestTest6 extends BaseModel<TestTest6> {
    @Property(displayName = "名称")
    private String name;

    @Property(displayName = "编码")
    private String code;

    @ManyToMany
    @JoinTable(name = "test_6_and_test_7_rel",joinColumns = @JoinColumn(name = "test_test_6_id",nullable = false),
            inverseJoinColumns = @JoinColumn(name = "test_test_7_id",nullable = false))
    @Property(displayName = "测试ER模型7")
    private List<TestTest7> testTest7List;
}

com.sie.app.demo/model/TestTest7.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@Model(name = "test_test_7", displayName = "测试ER模型7")
public class TestTest7 extends BaseModel<TestTest6> {
    @Property(displayName = "名称")
    private String name;
   
    @Property(displayName = "编码")
    private String code;

    @ManyToMany
    @JoinTable(name = "test_6_and_test_7_rel",joinColumns = @JoinColumn(name = "test_test_6_id",nullable = false),
            inverseJoinColumns = @JoinColumn(name = "test_test_6_id",nullable = false))
    private List<TestTest6> testTest6List;
}

解决方案

应用目录栏缺失(切勿重启后端)

  1. 已安装应用 卸载对应应用
  2. 应用市场 导入jar包
  3. 应用市场 上架
  4. 应用市场 安装
  5. 切换用户

尝试更新应用

Day04

2024.10.26

模型扩展与继承

com.sie.app.demo/model/TestUser.java

1
2
3
4
5
6
7
8
9
10
11
12
@Model(name = "test_user", description = "测试用户")
public class TestUser extends BaseModel<TestUser> {
    @Property(columnName = "name", displayName = "名称",displayForModel = true)
    //@Validate.NotBlank
    private String name;

    @Validate.Email(message = "邮箱格式不正确")
    @Property(columnName = "email", displayName = "邮箱")
    private String email;

...
}

模型扩展

  • 如果模型的 name 与 parent 相同,就是扩展原来的模型。引擎只会有一个模型。  
  • 扩展模型可以给模型增加属性、服务。

com.sie.app.demo/model/TestUser2.java

1
2
3
4
5
6
7
8
@Model(name = "test_user", parent = "test_user")
public class TestUser2 extends BaseModel<TestUser2> {
@Property(displayName = "扩展aaa")
private String aaa;

@Property(displayName = "扩展bbb")
private String bbb;
}

模型继承

  • 如果模型的 name 与 parent 不相同,就是继承原来的模型。引擎会有两个不同的模型。
  • 继承模型可以复用父模型的属性、服务。

com.sie.app.demo/model/TestUser3.java

1
2
3
4
5
6
7
8
@Model(name = "test_user_3", parent = "test_user")
public class TestUser3 extends BaseModel<TestUser3> {
@Property(displayName = "儿子aaa")
private String aaa1;

@Property(displayName = "儿子bbb")
private String bbb;
}

==父类若有扩展,扩展的属性也会被继承==
==若扩展的属性与子类新属性重名,优先使用子类的属性。==

开发者中心-模型管理

只会出现两个类: test_user test_user_3

test_user内含:

  1. TestUser.java属性
  2. TestUser2.java属性:扩展aaa(aaa),扩展bbb(bbb)

test_user_3内含:

  1. 继承test_user不重名属性
  2. TestUser3.java属性:儿子aaa(aaa1)

由于儿子bbb(bbb)与扩展bbb(bbb)重复,优先保留儿子bbb(bbb)

模型服务

声明服务

在模型写一个方法,并且加上 @MethodService 标记这个方法成为一个模型服务。模型的服务可以作为 API 被前端调用。
  服务需要写明几个信息
  1. name 服务名称
  2. auth 权限点
  3. description 服务描述
com.sie.app.demo/model/TestMethodService.java

1
2
3
4
5
6
7
@Model(name = "test_method_service")
public class TestMethodService extends BaseModel<TestMethodService>) {
@MethodService(name = "hello", description = "say hello")
public String hello(String name){
return "Hello World!"+name;
}
}

调试接口

在租户端使用demo应用中服务模板修改

测试基本类型-F12-Network

找到调用服务的Post转发

右键复制为bash

1
2
3
4
```

`Postman客户端-import-Raw text`
粘贴
1
2
3
4
5
6
7
8
9
10

修改Body部分
参数
```json
"args":{
"name": "eason"
}

"model": "test_method_service",
"service": "hello"

修改发送网址model,service


扩展服务

在声明模型的时候,我们的模型继承了 BaseModel。引擎会自动给模型加上基础增删改查的服务。

一般来说自带的服务都能满足业务要求。如果模型的服务不满足业务要求的时候,我们可以扩展原来的服务。

我们只需要在模型中声明相同方法签名的服务,然后加入新增的逻辑。

在扩展的服务中,可以使用 getMeta().get(modelname).callSuper() 方法调用模型扩展前的服务。


com.sie.app.demo/model/TestCurdMethod.java

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
@MethodService(name = "create",auth = "create",description = "创建")
public RecordSet create(RecordSet rs, List<Map<String, Object>> valueList){
    System.out.println("创建方法入参"+valueList);
    RecordSet rsc = (RecordSet) rs.callSuper(TestCurdMethod.class,"create",valueList);
    return rsc;
}

@MethodService(name = "update",auth = "update", description = "更新")
public RecordSet update(RecordSet rs,Map<String,Object> values){
    System.out.println("更新方法入参"+values);
    RecordSet rec = (RecordSet) rs.callSuper(TestCurdMethod.class,"update",values);
    return rec;
}

@MethodService(name = "delete",auth = "delete",description = "删除")
public boolean delete(RecordSet rs){
    System.out.println("删除方法入参"+rs.getId());
    rs.callSuper(TestCurdMethod.class,"delete");
    return Boolean.TRUE;
}

@MethodService(name = "search", auth = "search",description = "分页查询方法")
public List<Map<String,Object>> search(RecordSet rs, Filter filter,
                                 List<String> properties,
                                 Integer limit,Integer offset,String order){
    List<Map<String,Object>> result =
        (List<Map<String, Object>>) rs.callSuper(TestCurdMethod.class,"search",filter,properties,limit,offset,order);
    System.out.println("分页查询方法返回参数"+result);
    return result;
}

生成视图
com.sie.app.demo/views/test-curd-method.json

断点测试CURD

Filter

条件
import com.sie.snest.engine.rule.Filter;

在服务中,通常需要调用 search 方法进行查询数据。search 方法第一个参数是 Filter 类。Filter 用于构建查询条件。

1
2
3
4
5
6
7
8
9
10
11
12
Filter.equal("name", name);            # name = “name”
Filter.notEqual("name", name); # name <> “name”
Filter.like("name", name); # name like “%name%”
Filter.notLike("name", name); # name not like “%name%”
Filter.rlike("name", name); # name like “name%”
Filter.llike("name", name); # name like “%name”
Filter.greater("age", 10); # age > 10
Filter.greaterOrEqual("age", 10); # age >= 10
Filter.less("age", 10); # age < 10
Filter.lessOrEqual("age", 10); # age <= 10
Filter.in("name", Arrays.asList("张三", "李四")); # name in (“张三”, “李四”)
Filter.notIn("name", Arrays.asList("张三", "李四")); # name not in (“张三”, “李四”)
逻辑运算

Filter 提供 AND 和 OR 方法,用于构建 SQL 中的 and 和 or 功能。

1
2
3
4
Filter a = Filter.equal("name", name);
Filter b = Filter.notEqual("name", name);
Filter c = Filter.AND(a, b); # a and b
Filter d = Filter.OR(a, b); # a or b
前端表示

7.png

1
2
3
4
5
6
7
"filter": [ 
"&",
["name", "=", "张三"],
"|",
["age", ">", 10],
["id", "in", [1, 2]]
]

Day05

2024.10.27

需求实现

文件结构

mes
├── model
│   ├── Material.java
│   ├── Order.java
│   ├── Process.java
│   ├── ProcessBom.java
│   ├── ProcessRoute.java
│   ├── Product.java
│   ├── ProductBom.java
│   └── Supplier.java
├── views
│   ├── menus.json
│   ├── mes-material-view.json
│   ├── mes-order-view.json
│   ├── mes-process-bom-view.json
│   ├── mes-process-route-view.json
│   ├── mes-process-view.json
│   ├── mes-product-bom-view.json
│   ├── mes-product-view.json
│   └── mes-supplier-view.json
└── app.json

模型定义

Material.java

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
@Model(name = "material",
tableName = "material",
displayName = "物料",
description = "物料信息",
isAutoLog = Bool.True
)
public class Material extends BaseModel<Material> {
@Property(displayName = "名称",displayForModel = true)
private String name;

@Property(displayName = "编码")
private String code;

@Dict(typeCode = "material_type")
@Property(displayName = "类型")
private String type;

@Dict(typeCode = "material_unit")
@Property(displayName = "单位")
private String unit;

@Property(displayName = "描述")
private String description;

@Property(displayName = "库存量")
private Integer stockQuantity;

@Property(displayName = "单价")
private Integer price;

@ManyToOne(displayName = "供应类")
@JoinColumn
private Supplier supplier;
}

Order.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Model(name = "order",displayName = "生产订单",isAutoLog = Bool.True)
public class Order extends BaseModel<Order> {
@ManyToOne
@JoinColumn
private Product product;

@Property(displayName = "数量")
private Integer quantity;

@Property(displayName = "订单状态",defaultValue = "1",widget = "select",length = 256)
@Selection(values = {@Option(label = "设计中",value = "1"),
@Option(label = "生产中",value = "2"),
@Option(label = "已完成",value = "3")})
private String status;

@Property(displayName = "预计完成时间",dataType = DataType.DATE_TIME)
private Date dueDate;
}

Process.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Model(name = "process",displayName = "工序",isAutoLog = Bool.True)
public class Process extends BaseModel<Process> {
@Property(displayName = "名称")
private String name;
@Property(displayName = "编码")
private String code;

@ManyToOne
@JoinColumn
private ProcessRoute processRoute;

@OneToMany
private List<ProcessBom> processBomList;
}

ProcessBom.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@Model(name = "process_bom",displayName = "工序BOM",isAutoLog = Bool.True)
public class ProcessBom extends BaseModel<ProcessBom> {
@ManyToOne(displayName = "工序")
@JoinColumn
private Process process;

@ManyToOne(displayName = "物料")
@JoinColumn
private Material material;

@Property(displayName = "数量")
private Double amount;
}

ProcessRoute.java

1
2
3
4
5
6
7
8
9
10
11
12
@Model(name = "process_route",displayName = "工艺路线", isAutoLog = Bool.True)
public class ProcessRoute extends BaseModel<ProcessRoute> {
@Property(displayName = "名称")
private String name;

@Property(displayName = "编码")
private String code;

@ManyToOne
@JoinColumn
private Product product;
}

Product.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Model(name = "product", description = "产品", displayName = "产品",isAutoLog = Bool.True)
public class Product extends BaseModel<Product> {
@Validate.NotBlank(message = "产品名称不能为空!")
@Property(displayName = "名称")
private String name;

@Validate.NotBlank(message = "产品名称不能为空!")
@Property(displayName = "编码")
private String code;

@Property(displayName = "产品状态",defaultValue = "1",widget = "select",length = 256)
@Selection(values = {@Option(label = "设计中",value = "1"),
@Option(label = "生产中",value = "2"),
@Option(label = "已完成",value = "3")})
private String status;

@OneToMany
private List<ProductBom> productBomList;
}

ProductBom.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Model(name = "product_bom", description = "产品BOM", displayName = "产品BOM",isAutoLog = Bool.True)
public class ProductBom extends BaseModel<ProductBom> {

@ManyToOne(displayName = "产品")
@JoinColumn
private Product product;

@ManyToOne(displayName = "物料")
@JoinColumn
private Material material;

@Property(displayName = "数量")
private Integer amount;
}

Supplier.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Model(name = "supplier",displayName = "供应类",isAutoLog = Bool.True)
public class Supplier extends BaseModel<Supplier> {
@Property(displayName = "名称")
private String name;

@Property(columnName = "phone", displayName = "联系方式")
@Validate.Phone(message = "联系方式不正确")
private String phone;

@Property(displayName = "地址")
private String address;

@OneToMany
private List<Material> materialList;
}

视图定义

生成视图


小任务

7.1 说明

  为了更熟悉 IIDP 开发的流程,可以从下面四个实训任务选取一个进行开发。

7.2 产品BOM 管理功能

  需求
  1.完成产品管理页面,有产品名称、规格等信息
  2.完成物料管理页面,有物料名称、物料编码(编码唯一)等信息。
  3.完成产品BOM标签页。显示产品关联的物料。产品 BOM 有数量属性。
  4.实现产品 BOM 的添加、删除功能。

  提示
  1.使用 @Unique 实现编码唯一校验

7.3 编码规则功能

  需求
  1.使用 IIDP 实现一个编码规则功能。具有增删改查功能。
  2.编码规则包含前缀、流水号位数、日期格式。
  3.日期格式是下拉选择。有年月日、年月、年三种选项。
  4.提供一个服务,可以按指定编码规则生成一个编码。例如前缀 PM、流水号位数 6、日期格式为年月日。调用该服务,返回一个流水号,PM20231201000001。再次调用返回连续流水号的编码。

  提示
  1.使用 @Selection 实现下拉选择

7.4 数据隔离

  需求
  1.开发一个模型,实现增删改查功能。属性包含创建人、创建时间、修改人、修改时间。
  2.用户只能查看自己创建的数据。
  3.登录另一账号,不能查看到其他用户的数据。

  提示
  1.使用 isAutoLog 开启审计字段
  2.创建人属性名为 create_user

7.5 服务扩展

  需求
  1.开发一个APP1,有一个订单模型。含有字段发货时间。订单模型具有一个“发货”服务。
  2.实现简单增删改查功能。
  3.调用服务,将发货时间改成当前时间。
  4.开发另一个 APP2。扩展订单模型的发货服务。逻辑为调用服务时,将发货时间改成当前时间+24小时。
  5.通过应用市场安装 APP2,点击“发货”按钮。订单的发货时间被修改成当前时间+24小时。

  提示
  1.使用服务扩展