Kaynağa Gözat

some changes

master
qdjjx 3 yıl önce
ebeveyn
işleme
515a7cc103

+ 74
- 0
专题/后端/2021.03.01新总部运营后台适配办公楼版.md Dosyayı Görüntüle

@@ -0,0 +1,74 @@
1
+> 需要解决的问题是什么?
2
+
3
+新的总部运营后台适配办公楼版。从数据库来说,老的总部运营后台用到了`authserver`和`hd`这两个数据库。而新的总部运营后台用到了`hd_new`这个数据库。
4
+
5
+
6
+
7
+从基础设施层来看,原先所有和`authserver`和`hd`相关的增删改查都要切换到`hd_new`这个数据库。
8
+
9
+
10
+
11
+> 大致步骤
12
+
13
+- 找出所有和`IHeadQuarterRepository`和`IAuthRepository`
14
+- 替换成`HdContext`上下文下的表
15
+
16
+
17
+
18
+> 原则
19
+
20
+- 不改变接口
21
+- 做基础设施层
22
+- 在Application层加上`TODO`或自己改
23
+
24
+
25
+
26
+> `authserver`数据库
27
+
28
+- `aspnetusers`:用户表
29
+
30
+```
31
+-- UserName:在办公楼版中是手机号
32
+-- ProjectIds:通常用来表示用户属于哪个项目,但在办公楼版的后期没有用到,用户和项目的关系放在了projectuserroles表
33
+-- RemarkName:被用做了办公楼版的昵称
34
+-- Avator:被用做了办公楼版的头像
35
+```
36
+
37
+- `aspnetroles`: 角色表
38
+
39
+```
40
+与办公楼版有关的几个角色,分别是:
41
+-- HdDg:表示总部电工,也叫做办公楼版的超级管理员。总部电工/超级管理员登录办公楼版APP的时候,在"我的"里面可以选择项目下的某个公司,然后向这个公司的某个人A(想成为OfficeAdmin的那个人)展示二维码,扫描二维码以后,A就成为新朋友,然后总部电工/超级管理员在新朋友列表中找到A,点击同意,把他设置成公司管理员,即OfficeAdmin。其中相关的表用到了:office_demo中的companyusers,和authserver中的projectuserroles表
42
+-- OfficeAdmin:表示办公楼版的管理员。在办公楼版中,一个项目下有多个公司,每个公司只能有1个管理员,但是管理员可以在多个公司。
43
+-- OfficeGuest:表是办公楼版的游客。当某个注册用户,即在authserver中的aspnetusers表中有记录的用户扫描二维码,他的默认角色就是OfficeGuest
44
+-- OfficeOperator: 表示办公楼版的操作员。操作员不仅可以查看信息,还可以修改开关名称,控制开关,延时断电。
45
+```
46
+
47
+- `aspnetuserroles`:用户角色中间表,后期没有用到。把用户、角色与项目和公司的关系放到了`projectuserroles`表中
48
+- `projectuserroles`表:用来存放用户、角色与项目和公司的关系,具体什么时候用到表,要看具体业务逻辑。
49
+
50
+
51
+
52
+> `hd`数据库
53
+
54
+
55
+
56
+- `projects`表:有关项目
57
+- `groups`表:集团
58
+- `macprojects`: 模块和项目的关系
59
+- `banners`表示项目的banner管理
60
+- `industries`表是行业
61
+- `warningpolicies`表示报警策略
62
+
63
+
64
+
65
+> `hd_new`数据库
66
+
67
+
68
+
69
+
70
+
71
+# 
72
+
73
+
74
+

+ 0
- 0
专题/后端/2021.03.08数据库迁移.md Dosyayı Görüntüle


+ 8
- 0
专题/后端/2021.2.3好用的sql语句.md Dosyayı Görüntüle

@@ -13,5 +13,13 @@ select count(*) from breakerdatas where mac='98CC4D213003'
13 13
 delete from breakerdatas where mac='187ED53338C4' and str_to_date(CreateTime, '%Y-%m-%d %H:%i:%s') <= str_to_date('2021-02-23 18:54:00', '%Y-%m-%d %H:%i:%s')
14 14
 
15 15
 select CreateTime, mac, addr from  breakerdatas where mac='187ED53338C4' and addr='1' and str_to_date(CreateTime, '%Y-%m-%d %H:%i:%s') >= str_to_date('2021-02-23 19:30:00', '%Y-%m-%d %H:%i:%s')
16
+
17
+select * from  breakerdatas where mac='187ED53338C4' and str_to_date(CreateTime, '%Y-%m-%d %H:%i:%s') >= str_to_date('2021-02-23 19:30:00', '%Y-%m-%d %H:%i:%s')
18
+
19
+select * from  breakerdatas where mac='187ED53338C4' and str_to_date(CreateTime, '%Y-%m-%d %H:%i:%s') >= date_sub(now(), interval 1 hour)
20
+
21
+select mac, addr, CreateTime from breakerdatas where mac='187ED53338C4' and addr='1' ORDER BY CreateTime desc
22
+
23
+select count(*) from breakerdatas where mac='187ED53338C4' and addr='1'
16 24
 ```
17 25
 

+ 19
- 0
专题/后端/DDD/10、MedatR的两种用途.md Dosyayı Görüntüle

@@ -0,0 +1,19 @@
1
+如果是请求响应模式
2
+
3
+```
4
+IRequest
5
+IRequestHandler
6
+
7
+_mediator.Send(IRequest);
8
+```
9
+
10
+如果是事件广播模式
11
+
12
+```
13
+INotification
14
+INotificationHandler
15
+
16
+_mediator.Publish(INotification);
17
+```
18
+
19
+而`IMediator`可以被注入到任何类中。

+ 160
- 0
专题/后端/Starbust/01AntJob的运作原理.md Dosyayı Görüntüle

@@ -0,0 +1,160 @@
1
+# 表结构
2
+
3
+```
4
+//每一个应用实例就是一个App
5
+public class App
6
+{
7
+	public int ID {get;set;}
8
+	publi string DisplayName {get;set;}
9
+	public string Secret {get;set;}	
10
+}
11
+
12
+//描述App的工作状态
13
+public class AppOnline
14
+{
15
+	public int ID {get;set;}
16
+	public string Instantce {get;set;}
17
+	public string Client {get;set;}
18
+	public string Name {get;set;}
19
+	public int ProcessId {get;set;}
20
+	public string Version {get;set;}
21
+	public string Server {get;set;}
22
+	public int Tasks {get;set;}
23
+	public int Total {get;set;}
24
+}
25
+
26
+//作业
27
+public class Job
28
+{
29
+	public int ID {get;set;}
30
+	public int AppID {get;set;}
31
+	public string Name {get;set;}
32
+	public string ClassName {get;set;}
33
+	public string DisplayName {get;set;}
34
+	public int Mode {get;set;}
35
+	public string Topic {get;set;}
36
+	public int MessageCount {get;set;}
37
+	public DateTime Start {get;set;}
38
+	public DateTime End {get;set;}
39
+	public int Step {get;set;}
40
+	public int BatchSize {get;set;}
41
+	public int Offset {get;set;}	
42
+}
43
+
44
+//作业的任务
45
+public class JobTask
46
+{
47
+	public int ID {get;set;}
48
+	public int AppID {get;set;}
49
+	public int JobID {get;set;}
50
+}
51
+```
52
+
53
+# 应用、调度中心、Web控制台
54
+
55
+![](F:\SourceCodes\DDWiki\专题\后端\Starbust\AntJob.svg)
56
+
57
+- 计算应用:理解成一个应用实例,可能是本地开发环境的控制台程序,可能是服务器上的一个服务
58
+- 调度中心:调度所有的作业,需要被持久化
59
+- Web控制台:展示调度中心的持久化内容
60
+
61
+# 最简单的例子
62
+
63
+应用要做的事情是把作业交给调度器开始执行。
64
+
65
+```
66
+            var set = AntSetting.Current;
67
+
68
+            // 实例化调度器
69
+            var sc = new Scheduler();
70
+
71
+            // 使用分布式调度引擎替换默认的本地文件调度
72
+            sc.Provider = new NetworkJobProvider
73
+            {
74
+                Server = set.Server,
75
+                AppID = set.AppID,
76
+                Secret = set.Secret,
77
+            };
78
+
79
+            // 添加作业处理器
80
+            sc.Handlers.Add(new HelloJob());
81
+            sc.Handlers.Add(new BuildPatient());
82
+            sc.Handlers.Add(new BuildWill());
83
+
84
+            // 启动调度引擎,调度器内部多线程处理
85
+            sc.Start();
86
+```
87
+
88
+所有的作业都是`Handler`类型。
89
+
90
+```
91
+	[DisplayName("定时欢迎")]
92
+    [Description("简单的定时任务")]
93
+    internal class HelloJob : Handler
94
+    {
95
+        public HelloJob()
96
+        {
97
+            // 今天零点开始,每10秒一次
98
+            var job = Job;
99
+            job.Start = DateTime.Today;
100
+            job.Step = 10;
101
+        }
102
+
103
+        protected override Int32 Execute(JobContext ctx)
104
+        {
105
+            // 当前任务时间
106
+            var time = ctx.Task.Start;
107
+            WriteLog("新生命蚂蚁调度系统!当前任务时间:{0}", time);
108
+
109
+            // 成功处理数据量
110
+            return 1;
111
+        }
112
+    }
113
+```
114
+
115
+![antjob1](F:\SourceCodes\DDWiki\专题\后端\Starbust\antjob1.png)
116
+
117
+# `Scheduler.Start()`做了什么?
118
+
119
+```
120
+public class Scheduler
121
+{
122
+	public List<Handler> Handlers { get; } = new List<Handler>();
123
+	public IJobProvider Provider { get; set; }//作业提供者
124
+	
125
+	public void Start()
126
+	{
127
+		var prv = Provider;
128
+		prv.Schedule = this;
129
+		prv.Start();//作业提供者开始
130
+		
131
+		var jobs = prv.GetJobs();
132
+		
133
+		var hs = Handlers;
134
+		foreach (var handler in hs)
135
+		{
136
+			handler.Schedule = this;
137
+			handler.Provider = prv;
138
+			
139
+			var job = jobs.FirstOrDefault(e => e.Name == handler.Name);
140
+			if(job!=null && job.Mode ==0) job.Mode = handler.Mode;
141
+			handler.Job = job;
142
+			
143
+			handler.Start();//作业处理器开始
144
+		}
145
+		
146
+		if(Period > 0) _timer = new TimeX(Loop, null, Period * 1000, "Job"){Async = true};
147
+	}
148
+}
149
+```
150
+
151
+`Scheduler`中维护着`Handler`的集合以及`IJobProvider`。`IJobProvider`需要知道`Scheduler`。`Handler`需要知道`Scheduler`和`IJobProvider`以及`IJob`。`Scheduler`让`IJobProvider`和每个`Handler`开始工作。更加重要的是,在实例化`TimeX`的过程中开始了任务调度,即`Scheudler`中的`Process()`。
152
+
153
+
154
+
155
+`Pr`
156
+
157
+
158
+
159
+
160
+

+ 1
- 0
专题/后端/Starbust/AntJob.svg
Dosya farkı çok büyük olduğundan ihmal edildi
Dosyayı Görüntüle


BIN
专题/后端/Starbust/antjob1.png Dosyayı Görüntüle


+ 337
- 0
专题/后端/数据/数据问题的基本面.md Dosyayı Görüntüle

@@ -0,0 +1,337 @@
1
+# 存储过程的使用
2
+
3
+使用`EF`结合存储过程。用`EF`生成数据库、数据库迁移,查询用存储过程。更新、添加、删除之类的用`EF`。
4
+
5
+# 升级
6
+
7
+- 将来有一个注册中心可以进行远程发布
8
+
9
+
10
+
11
+# 实时数据
12
+
13
+
14
+
15
+> 使用场景包括:
16
+
17
+- 当对开关进行合闸分断后,需要根据实时数据来判断是否操作成功
18
+- 当对报警进行判断的时候,需要根据实时数据来判断报警是否解除
19
+
20
+
21
+
22
+
23
+
24
+>  实时数据从哪里来?
25
+
26
+每一个模块(通讯模块)可以指定服务器`IP`,实际上是指向到服务器上的一个`API`接口,然后在接口里对实时数据进行处理。比如说,指向到`47.103.61.198:5008/ebx-bishop/data/carry`。
27
+
28
+
29
+
30
+> 实时数据到哪里去?
31
+
32
+- 最新的实时数据进入了`Redis`缓存
33
+
34
+关于键和命名空间
35
+
36
+```
37
+OpenAPI_Test:187ED53159E4,macss 就是缓存的键。其中OpenAPI_Test可以理解为是命名空间,而且是可以多级的,比如:OpenAPI_Test:Test2
38
+```
39
+
40
+实时数据长啥样?
41
+
42
+```
43
+{
44
+  "serverinfo": {
45
+    "port": "12345",
46
+    "hardware": "T25",
47
+    "execleakcheck": "false",
48
+    "mac": "187ED53159E4", //模块号,是唯一的
49
+    "loginid": "187ED53159E4",
50
+    "gate": "",
51
+    "ip": "192.168.8.151",
52
+    "version": "2.52",
53
+    "loginpwd": "7a57a5a743894ae4",
54
+    "ssidpwd": "2803f88db851c67a",
55
+    "ssid": "DDINGW",
56
+    "timezoneId": "Asia/Shanghai",
57
+    "server": "http://47.103.61.198:5008/snd",
58
+    "datetime": "2021-02-25 02:10:00", //上传时间,会根据这个字段判断是否离线
59
+    "leakcheckdate": "3,13,33",
60
+    "lastleakcheckdate": "2017-01-01 10:00:00"
61
+  },
62
+  "distributbox": {
63
+    "Breakers": { //开关
64
+      "1": {//线路地址1
65
+        "version": "0.72",
66
+        "title": "总路",
67
+        "model": "JZK2L100-BL6523",
68
+        "Alarm": 0,
69
+        "EnableNetCtrl": true,//是否可以通过软件控制线路,比如线下某个电工手动把分断了,那么这里的EnableNetCtrl=false,这时候,如果软件界面上需要合闸,需要判断EnableNetCtrl这个字段值,如果是EnableNetCtrl=false情况下不运行进行合闸,避免导致电工的生命安全,需要自己写逻辑判断
70
+        "MXDW": 0.0,//门限低温,当温度低于这里的设置,模块会主动推送报警,服务端来接收,门限值常见会提供接口进行修改
71
+        "MXGG": 7040.0,//门限过功,门限值常见会提供接口进行修改
72
+        "MXGL": 48.00,//门限过流,门限值常见会提供接口进行修改
73
+        "MXGW": 90.0,//门限过温,门限值常见会提供接口进行修改
74
+        "MXGY": 260.0,//门限过压,门限值常见会提供接口进行修改
75
+        "MXLD": 30.0,//门限漏电,门限值常见会提供接口进行修改
76
+        "MXQY": 100.0,//门限欠压,门限值常见会提供接口进行修改
77
+        "OpenClose": true,//开关的合闸分断状态,true表示当前合闸状态
78
+        "addr": 1,//线路地址
79
+        "power": 51.3,//上一个小时的电量,单位是瓦W
80
+        "specification": "32",//线路规格
81
+        "control": 1,//线路是否可控,如果是0的话,线路无法合闸分断,在实践中再去体会
82
+        "visibility": 1,//线路是否可见
83
+        "totalChannelId": -1,//是否是总路,本开关线路是被接入到哪个开关线路下(-1 - 进线直连,即本开关未被接入到其它开关;其它 - 本开关线路被接入到另一个开关线路,此值为本开关所接入的开关线路的地址编号),需要实际施工的时候区分连接方式
84
+        "lineType": "220",//线路类型,220表示接的220V的电压;380表示接入的是380V的三相电压
85
+        "A_A": 0.13,//三相情况下的A相电流,在lineTYpe=220的情况下,就是线路电流
86
+        "A_T": 27.5,//线温
87
+        "A_V": 232.0,//电压
88
+        "A_WP": 25.0,//功率
89
+        "A_LD": 0.0,//漏电
90
+        "A_PF": 0.0,//功率因素
91
+        "G_A": 0.0,//平均电流
92
+        "G_T": 0.0,
93
+        "G_V": 0.0,
94
+        "G_WP": 0.0,
95
+        "G_LD": 0.0,
96
+        "G_PF": 0.0,
97
+        "B_A": 0.0,//B相
98
+        "B_T": 0.0,
99
+        "B_V": 0.0,
100
+        "B_WP": 0.0,
101
+        "B_PF": 0.0,
102
+        "C_A": 0.0,//相
103
+        "C_T": 0.0,
104
+        "C_V": 0.0,
105
+        "C_WP": 0.0,
106
+        "C_PF": 0.0,
107
+        "N_A": 0.0,//零线电流
108
+        "N_T": 0.0//零线线温
109
+      },
110
+      "2": {
111
+        "version": "0.72",
112
+        "title": "线路1",
113
+        "model": "JZK2L100-BL6523",
114
+        "Alarm": 0,
115
+        "EnableNetCtrl": true,
116
+        "MXDW": 0.0,
117
+        "MXGG": 7040.0,
118
+        "MXGL": 48.00,
119
+        "MXGW": 90.0,
120
+        "MXGY": 260.0,
121
+        "MXLD": 100.0,
122
+        "MXQY": 100.0,
123
+        "OpenClose": true,
124
+        "addr": 2,
125
+        "power": 4.7,
126
+        "specification": "32",
127
+        "control": 1,
128
+        "visibility": 1,
129
+        "totalChannelId": 1,
130
+        "lineType": "220",
131
+        "A_A": 0.00,
132
+        "A_T": 26.4,
133
+        "A_V": 234.0,
134
+        "A_WP": 0.0,
135
+        "A_LD": 0.0,
136
+        "A_PF": 0.0,
137
+        "G_A": 0.0,
138
+        "G_T": 0.0,
139
+        "G_V": 0.0,
140
+        "G_WP": 0.0,
141
+        "G_LD": 0.0,
142
+        "G_PF": 0.0,
143
+        "B_A": 0.0,
144
+        "B_T": 0.0,
145
+        "B_V": 0.0,
146
+        "B_WP": 0.0,
147
+        "B_PF": 0.0,
148
+        "C_A": 0.0,
149
+        "C_T": 0.0,
150
+        "C_V": 0.0,
151
+        "C_WP": 0.0,
152
+        "C_PF": 0.0,
153
+        "N_A": 0.0,
154
+        "N_T": 0.0
155
+      },
156
+      "3": {
157
+        "version": "0.72",
158
+        "title": "线路2",
159
+        "model": "JZK2L100-BL6523",
160
+        "Alarm": 0,
161
+        "EnableNetCtrl": true,
162
+        "MXDW": 0.0,
163
+        "MXGG": 7040.0,
164
+        "MXGL": 48.00,
165
+        "MXGW": 90.0,
166
+        "MXGY": 260.0,
167
+        "MXLD": 30.0,
168
+        "MXQY": 100.0,
169
+        "OpenClose": true,
170
+        "addr": 3,
171
+        "power": 40.6,
172
+        "specification": "32",
173
+        "control": 1,
174
+        "visibility": 1,
175
+        "totalChannelId": 1,
176
+        "lineType": "220",
177
+        "A_A": 0.11,
178
+        "A_T": 28.2,
179
+        "A_V": 235.0,
180
+        "A_WP": 22.0,
181
+        "A_LD": 0.0,
182
+        "A_PF": 0.0,
183
+        "G_A": 0.0,
184
+        "G_T": 0.0,
185
+        "G_V": 0.0,
186
+        "G_WP": 0.0,
187
+        "G_LD": 0.0,
188
+        "G_PF": 0.0,
189
+        "B_A": 0.0,
190
+        "B_T": 0.0,
191
+        "B_V": 0.0,
192
+        "B_WP": 0.0,
193
+        "B_PF": 0.0,
194
+        "C_A": 0.0,
195
+        "C_T": 0.0,
196
+        "C_V": 0.0,
197
+        "C_WP": 0.0,
198
+        "C_PF": 0.0,
199
+        "N_A": 0.0,
200
+        "N_T": 0.0
201
+      }
202
+    },
203
+    "Change": null
204
+  }
205
+}
206
+```
207
+
208
+总之,`Redis`中的实时数据理解为模块最近一次上传的实时数据;如果需要最实时的数据,直接调用接口获取。
209
+
210
+
211
+
212
+- 最近60分钟的数据进入`MySQL`数据库
213
+
214
+`Redis`只能记录最近的一条记录,但是,在一些场景下需要最近比如60分钟的数据。举例来说,在负荷报表中希望看到最近60分钟的负荷情况。这时,需要把最近60分钟的数据保存到`MySQL`数据库。
215
+
216
+- 每一次的上传数据进入时序数据库
217
+
218
+
219
+
220
+> 实时数据的上传频率问题
221
+
222
+默认情况下模块每10分钟上传一次数据。如果想让模块以最快的频率上传数据,需要给模块发一条指令(有时需要同时发2条指令,第一条指令让模块保持登录态,另一条指令设置实时数据的上传频率)。而这条执行的生效时长是5分钟。
223
+
224
+
225
+
226
+# 电量数据
227
+
228
+
229
+
230
+> 原理
231
+
232
+模块每小时、每天、每月的开头会向服务器发送电量数据。服务器会把电量数据保存到时序数据库。
233
+
234
+在`UDP`协议中可以查询获取过去一个月的电量历史数据。在`HTTP`中主动查模块的电量数据暂时不确定。
235
+
236
+> 目前存在的问题
237
+
238
+电量数据会重复传或者漏传。
239
+
240
+
241
+
242
+#  实时调度平台
243
+
244
+
245
+
246
+目前采用`大石头`的`AntJob`组件。
247
+
248
+- 应用程序
249
+- 作业
250
+
251
+# 报警机制
252
+
253
+
254
+
255
+> 报警策略
256
+
257
+```
258
+报警策略名称:总部默认报警策略
259
+
260
+过压报警: 是否启用,阈值=230V
261
+过压预警:是否启用
262
+		蓝色预警:阈值=222V
263
+		黄色预警:阈值=225V   
264
+		橙色预警:阈值=228V  
265
+手动分断报警:是否启用
266
+离线报警:是否启用,阈值=10分钟
267
+```
268
+
269
+
270
+
271
+> 报警如何产生的?
272
+
273
+- 达到模块内部的报警条件触发报警,模块向服务器发送报警
274
+- 达到某种报警策略的报警触发条件进而报警,也就是在应用程序层面判断并记录
275
+
276
+
277
+
278
+> 报警和项目如何关联?
279
+
280
+在总部运营后台,关联报警策略和项目。而且,一个项目可以有多个报警策略。
281
+
282
+
283
+
284
+> 报警存入哪里?
285
+
286
+报警分为实时报警和历史报警。实时报警就是当下所有开关的报警;历史报警是发生在过去,报警已经解除的哪些报警。比如,开关A,在`8:10`程序判断出现手动分断报警,那么在实时报警中添加一条记录。接着,在`9:10`程序判断开关A已经正常,就在历史报警中添加一条记录,这条记录的报警开始时间是`8:10`,结束时间是`9:10`,并且在实时报警中更新该条记录,更新为开关A属于正常状态。
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
+- 有些模拟到关系数据库,有些模拟到时序数据库

BIN
团队/2021.3.5写字楼版和总部运营版的复盘(吕亮).docx Dosyayı Görüntüle


BIN
团队/2021.3.5复盘.xlsx Dosyayı Görüntüle


BIN
团队/鼎鼎团队协作原则和实践.docx Dosyayı Görüntüle


Loading…
İptal
Kaydet