目录比较的小工具myfc
因为工作需要,对遗留项目进行规范化整理,面对多个不同版本的程序,如何整理出一个完整的有效的代码?目前的工具比如ultra compare或者号称最好用的beyond compare,具有的只是目录比较和文件比较功能,其中目录比较只是简单比较文件的属性差异,不会比较目录中各个文件的差异,所以用了几天时间,修改完善了我自己的myfc,一个纯批处理文件,具有的功能如下:
1.比较两个目录的差异,支持目录的递归比较,判断标准是文件内容是否相同。
2.生成标准化的结果报告
小技巧:
1.如果加入-d参数,则会把所有的比较结果按照目录为单位输出到一个文件中。
2.对于*-diff.txt文件,可以用查找替换,把它变成一个bat文件,可以对有差异的文件用更高级的比较器进行比较。我经常会把diff.txt中的diff替换成call bc /w ,把 . 替换成rem,然后另存为bat格式。如果大家有要求,我考虑把它变成一个标准的输出格式
目前尚未实现的功能:
1. 除了文本之外的其他输出格式支持,比如xml或html的输出格式
下载代码如下:

Code
1
@echo off
2
rem ============================================================================
3
rem 本程序负责比较两个目录中的所有文件是否一致,并且按照一定的顺序把比较结果输出
4
rem 到文件中。程序的行为包括如下几个方面:
5
rem 1)遍历:仅仅当前目录还是需要遍历整个目录,用/s参数控制,附加的精细控制参数
6
rem 还包括/f和/x参数
7
rem 2)比较:单向比较还是双向比较,用/d参数控制。双向比较可以得到src-dir中缺失的
8
rem 所有文件列表,单向比较仅可以得到部分缺失的文件列表。
9
rem 2)显示:结果的显示包括原始格式输出、经过分类的输出。分类的输出有可以分输出
10
rem 到不同文件和同一文件。原始格式输出到*-all文件中,相同文件输出到*-same文件,
11
rem 不同文件输出到*-diff中,多余文件输出到*-redu,缺失文件输出到*-lost中。参数
12
rem /d控制把*-redu,*-lost,*-same,*-diff合并成一个输出*-dir文件。参数/q关闭控制
13
rem 台内容显示。
14
rem ============================================================================
15
16
rem 关闭cmd的扩展开关,保证生成临时bat的时候不出异常
17
set TSTEXT=a
18
if "!TSTEXT!"=="a" cmd.exe /V:OFF /C %0 %* & exit /b
19
set TSTEXT=
20
21
rem 保存当前工作目录
22
set PWD=%CD%
23
rem 如果第一个参数是DEBUG,表明打开调试选项
24
rem 需要设DEBUG变量,剩余命令行放在CMDLINE变量中
25
set DEBUG=
26
set CMDLINE0=%*
27
if #%1# == #DEBUG# set DEBUG=on
28
if #%1# == #debug# set DEBUG=on
29
if #%DEBUG%# == #on# set CMDLINE=%CMDLINE0:~5%
30
if NOT #%DEBUG%# == #on# set CMDLINE=%CMDLINE0%
31
set CMDLINE0=
32
if #%DEBUG%# == #on# shift
33
@if #%DEBUG%# == #on# @echo on
34
35
rem 是否显示帮助
36
if "%1" == "" goto help
37
if "%1" == "/?" goto help
38
if "%1" == "-?" goto help
39
if "%1" == "?" goto help
40
for %%a in (help HELP /help /HELP -help -HELP --help --HELP h H /h /H -h -H --h --H ) do if "%1" == "%%a" goto help
41
42
rem 开始参数识别和检测
43
set DIR1=
44
set DIR2=
45
46
set O_SUB=
47
set O_PAT=*.*
48
set O_EXC=
49
set O_DIR=
50
set O_QUITE=
51
set O_SUP=
52
set O_PRE=fc-o
53
set O_OVER=
54
set O_POST=txt
55
set O_CMDL=/n+/w
56
set O_MODI=
57
set O_VIEW=
58
if "%EDITOR%"=="" (set P_EDTR=notepad.exe) else (set P_EDTR=%EDITOR%)
59
60
:opt_an
61
for %%a in ( /s -s /S -S ) do if "%1" == "%%a" set O_SUB=y&& goto opt_nx
62
for %%a in ( /f -f /F -F ) do if "%1" == "%%a" goto opt_pat
63
for %%a in ( /x -x /X -X ) do if "%1" == "%%a" goto opt_exc
64
for %%a in ( /d -d /D -D ) do if "%1" == "%%a" set O_DIR=y&& goto opt_nx
65
for %%a in ( /q -q /Q -Q ) do if "%1" == "%%a" set O_QUITE=y&& goto opt_nx
66
for %%a in ( /u -u /U -U ) do if "%1" == "%%a" set O_SUP=y&& goto opt_nx
67
for %%a in ( /p -p /P -P ) do if "%1" == "%%a" goto opt_pre
68
for %%a in ( /o -o /O -O ) do if "%1" == "%%a" set O_OVER=y&& goto opt_nx
69
for %%a in ( /t -t /T -T ) do if "%1" == "%%a" set O_POST=htm&& goto opt_nx
70
for %%a in ( /c -c /C -C ) do if "%1" == "%%a" goto opt_cmd
71
for %%a in ( /m -m /M -M ) do if "%1" == "%%a" set O_MODI=y&& goto opt_nx
72
for %%a in ( /v -v /V -V ) do if "%1" == "%%a" set O_VIEW=y&& goto opt_nx
73
if "%DIR1%" == "" (for /d %%v in (%1) do set DIR1=%%~fv&&set DIR1S=%%~fsv) else (for /d %%v in (%1) do set DIR2=%%~fv&&set DIR2S=%%~fsv)
74
goto opt_nx
75
:opt_pat
76
shift
77
set O_PAT=%1
78
goto opt_nx
79
:opt_exc
80
shift
81
set O_EXC=%1
82
goto opt_nx
83
:opt_cmd
84
shift
85
set O_CMDL=%1
86
goto opt_nx
87
:opt_pre
88
shift
89
set O_PRE=%1
90
:opt_nx
91
shift
92
if not "%1" == "" goto opt_an
93
94
if "%DIR1%" == "" goto help
95
if "%DIR2%" == "" goto help
96
goto start
97
98
:help
99
echo -----======================================================================-----
100
echo usage: %~nx0 [/s] [/f pattern] [/x pattern] [/o] [/p prefix] src-dir dest-dir
101
echo /s 包含子目录中的文件
102
echo /f pattern 需要匹配的文件,多个必须用"+"号分隔,如 *.t*+b??.bat+a*.doc
103
echo /x pattern 排除的文件后缀名,多个必须用"+"号分隔,如 bak+tmp
104
echo /d 显示以目录为单位,并进行目录双向比较
105
echo /q 关闭控制台内容显示
106
echo /u 抑制不必要的输出(较慢)
107
echo /p prefix 输出文件名的前缀
108
echo /o 覆盖原有输出文件
109
echo /t 输出html格式的结果文件格式
110
echo /c param 传递给FC的参数,多个必须用"+"号分隔,比如/C+/N+/W
111
echo /m 脚本生成完毕,运行之前允许用%%EDITOR%%进行修改,默认notepad
112
echo /v 运行完毕之后自动打开结果文件
113
echo src-dir 用作比较的基准目录
114
echo dest-dir 用于比较的目录
115
goto end
116
117
:start
118
if not #%O_OVER%# == #y# goto app
119
if exist %PWD%\%O_PRE%-diff.%O_POST% del /f /q %PWD%\%O_PRE%-diff.%O_POST%
120
if exist %PWD%\%O_PRE%-same.%O_POST% del /f /q %PWD%\%O_PRE%-same.%O_POST%
121
if exist %PWD%\%O_PRE%-lost.%O_POST% del /f /q %PWD%\%O_PRE%-lost.%O_POST%
122
if exist %PWD%\%O_PRE%-redu.%O_POST% del /f /q %PWD%\%O_PRE%-redu.%O_POST%
123
if exist %PWD%\%O_PRE%-all.%O_POST% del /f /q %PWD%\%O_PRE%-all.%O_POST%
124
if exist %PWD%\%O_PRE%-dir.%O_POST% del /f /q %PWD%\%O_PRE%-dir.%O_POST%
125
:app
126
echo ===============================%DATE% %TIME%=========================== >> %PWD%\%O_PRE%-diff.%O_POST%
127
echo ===============================%DATE% %TIME%=========================== >> %PWD%\%O_PRE%-same.%O_POST%
128
echo ===============================%DATE% %TIME%=========================== >> %PWD%\%O_PRE%-lost.%O_POST%
129
echo ===============================%DATE% %TIME%=========================== >> %PWD%\%O_PRE%-redu.%O_POST%
130
echo ===============================%DATE% %TIME%=========================== >> %PWD%\%O_PRE%-all.%O_POST%
131
echo ===============================%DATE% %TIME%=========================== >> %PWD%\%O_PRE%-dir.%O_POST%
132
133
set RND=%TIME::=.%-%RANDOM%
134
set TEMP_BAT="%TEMP%\myfc-%RND%.bat"
135
rem ==========开始生成临时脚本==========
136
echo @echo off> %TEMP_BAT%
137
echo rem 本文件由myfc.bat自动生成>> %TEMP_BAT%
138
echo set SRC=%DIR1%>> %TEMP_BAT%
139
echo set DEST=%DIR2%>> %TEMP_BAT%
140
141
echo set B_OALL="%PWD%\%O_PRE%-all.%O_POST%">> %TEMP_BAT%
142
echo set B_ODIR="%PWD%\%O_PRE%-dir.%O_POST%">> %TEMP_BAT%
143
echo set B_ODIFF="%PWD%\%O_PRE%-diff.%O_POST%">> %TEMP_BAT%
144
echo set B_OSAME="%PWD%\%O_PRE%-same.%O_POST%">> %TEMP_BAT%
145
echo set B_OREDU="%PWD%\%O_PRE%-redu.%O_POST%">> %TEMP_BAT%
146
echo set B_OLOST="%PWD%\%O_PRE%-lost.%O_POST%">> %TEMP_BAT%
147
148
echo set B_TDIFF="%TEMP%\t%RND%-diff.txt">> %TEMP_BAT%
149
echo set B_TSAME="%TEMP%\t%RND%-same.txt">> %TEMP_BAT%
150
echo set B_TREDU="%TEMP%\t%RND%-redu.txt">> %TEMP_BAT%
151
echo set B_TLOST="%TEMP%\t%RND%-lost.txt">> %TEMP_BAT%
152
153
echo pushd %%SRC%%>> %TEMP_BAT%
154
echo call:walk_dir "%%SRC%%\">> %TEMP_BAT%
155
if "%O_SUB%" == "y" (set T_SUB=/r %%SRC%%) else (set T_SUB=)
156
echo for /d %T_SUB% %%%%v in (*.*) do call:walk_dir "%%%%~fv">> %TEMP_BAT%
157
set T_SUB=
158
echo popd>> %TEMP_BAT%
159
echo cd %PWD%>> %TEMP_BAT%
160
echo goto :EOF>> %TEMP_BAT%
161
162
echo :walk_dir>> %TEMP_BAT%
163
if not "%O_QUITE%" == "" goto skip_q1
164
echo echo %%1>> %TEMP_BAT%
165
:skip_q1
166
echo pushd %%1>> %TEMP_BAT%
167
168
echo if exist %%B_TSAME%% del /q /f %%B_TSAME%% ^> nul>> %TEMP_BAT%
169
echo if exist %%B_TDIFF%% del /q /f %%B_TDIFF%% ^> nul>> %TEMP_BAT%
170
echo if exist %%B_TREDU%% del /q /f %%B_TREDU%% ^> nul>> %TEMP_BAT%
171
echo if exist %%B_TLOST%% del /q /f %%B_TLOST%% ^> nul>> %TEMP_BAT%
172
173
echo for %%%%F in (%O_PAT:+= %) do ( >> %TEMP_BAT%
174
if not "%O_QUITE%" == "" goto skip_q2
175
echo echo %%%%F>> %TEMP_BAT%
176
:skip_q2
177
echo set B_EX=>> %TEMP_BAT%
178
if "%O_EXC%" == "" goto skip_e1
179
echo for %%%%e in (%O_EXC:+= %) do if "!B_EX!"=="" if ".%%%%e" == "%%%%~xF" set B_EX=y>> %TEMP_BAT%
180
:skip_e1
181
echo if "!B_EX!" == "" ( >> %TEMP_BAT%
182
echo set B_FN=%%%%~fF>> %TEMP_BAT%
183
echo set B_DFN=!B_FN:%DIR1%\=%DIR2%\!>> %TEMP_BAT%
184
echo call:out fc %O_CMDL:+= % !B_FN! !B_DFN! ^>^> %%B_OALL%%>> %TEMP_BAT%
185
echo if exist "!B_DFN!" ( >> %TEMP_BAT%
186
echo call:out %%%%~tF %%%%~zF %%%%~fF ^>^> %%B_OALL%%>> %TEMP_BAT%
187
echo for %%%%n in ("!B_DFN!") do ( >> %TEMP_BAT%
188
echo call:out %%%%~tn %%%%~zn %%%%~fn ^>^> %%B_OALL%%>> %TEMP_BAT%
189
echo )>> %TEMP_BAT%
190
echo fc %O_CMDL:+= % "!B_FN!" "!B_DFN!" ^>^> %%B_OALL%%>> %TEMP_BAT%
191
echo if #!errorlevel!# == #1# ( >> %TEMP_BAT%
192
echo call:out diff "!B_FN!" "!B_DFN!" ^>^> %%B_TDIFF%%>> %TEMP_BAT%
193
echo call:out . %%%%~tF %%%%~zF %%%%~fF ^>^> %%B_TDIFF%%>> %TEMP_BAT%
194
echo for %%%%n in ("!B_DFN!") do ( >> %TEMP_BAT%
195
echo call:out . %%%%~tn %%%%~zn %%%%~fn ^>^> %%B_TDIFF%%>> %TEMP_BAT%
196
echo )>> %TEMP_BAT%
197
echo )>> %TEMP_BAT%
198
echo if #!errorlevel!# == #0# ( >> %TEMP_BAT%
199
echo call:out same !B_FN! !B_DFN! ^>^> %%B_TSAME%%>> %TEMP_BAT%
200
echo )>> %TEMP_BAT%
201
echo ) else ( >> %TEMP_BAT%
202
echo call:out lost !B_DFN! ^>^> %%B_OALL%%>> %TEMP_BAT%
203
echo call:out lost !B_DFN! ^>^> %%B_TLOST%%>> %TEMP_BAT%
204
echo )>> %TEMP_BAT%
205
echo )>> %TEMP_BAT%
206
echo )>> %TEMP_BAT%
207
echo popd>> %TEMP_BAT%
208
209
if not "%O_DIR%" == "y" goto skip_d1
210
echo set B_SDIR=%%1>> %TEMP_BAT%
211
echo set B_DDIR=!B_SDIR:%DIR1%\=%DIR2%\!>> %TEMP_BAT%
212
echo if not exist %%B_DDIR%% goto no_redu>> %TEMP_BAT%
213
echo pushd %%B_DDIR%%>> %TEMP_BAT%
214
echo for %%%%F in (%O_PAT:+= %) do ( >> %TEMP_BAT%
215
echo set B_EX=>> %TEMP_BAT%
216
if "%O_EXC%" == "" goto skip_e2
217
echo for %%%%e in (%O_EXC:+= %) do if "!B_EX!"=="" if ".%%%%e" == "%%%%~xF" set B_EX=y>> %TEMP_BAT%
218
:skip_e2
219
echo if "!B_EX!" == "" ( >> %TEMP_BAT%
220
echo set B_1=%%%%~fF>> %TEMP_BAT%
221
echo set B_2=!B_1:%DIR2%\=%DIR1%\!>> %TEMP_BAT%
222
echo if not exist "!B_2!" ( >> %TEMP_BAT%
223
echo call:out redundant "%%%%~fF" ^>^> %%B_TREDU%%>> %TEMP_BAT%
224
echo )>> %TEMP_BAT%
225
echo )>> %TEMP_BAT%
226
echo )>> %TEMP_BAT%
227
echo for /d %%%%F in (*.*) do (>> %TEMP_BAT%
228
echo set B_1=%%%%~fF>> %TEMP_BAT%
229
echo set B_2=!B_1:%DIR2%\=%DIR1%\!>> %TEMP_BAT%
230
echo if not exist "!B_2!" call:fnList "%%%%~fF">> %TEMP_BAT%
231
echo )>> %TEMP_BAT%
232
echo )>> %TEMP_BAT%
233
echo popd>> %TEMP_BAT%
234
echo goto no_redu>> %TEMP_BAT%
235
echo :fnList>> %TEMP_BAT%
236
echo for /r %%1 %%%%G in (%O_PAT:+= %) do ( >> %TEMP_BAT%
237
echo set B_EX=>> %TEMP_BAT%
238
if "%O_EXC%" == "" goto skip_e3
239
echo for %%%%e in (%O_EXC:+= %) do if "!B_EX!"=="" if ".%%%%e" == "%%%%~xG" set B_EX=y>> %TEMP_BAT%
240
:skip_e3
241
echo if "!B_EX!" == "" ( >> %TEMP_BAT%
242
echo call:out redundant "%%%%~fG" ^>^> %%B_TREDU%%>> %TEMP_BAT%
243
echo )>> %TEMP_BAT%
244
echo )>> %TEMP_BAT%
245
echo goto :EOF>> %TEMP_BAT%
246
echo :no_redu>> %TEMP_BAT%
247
:skip_d1
248
249
echo echo %%1 ^>^> %%B_ODIR%%>> %TEMP_BAT%
250
251
echo if exist %%B_TSAME%% type %%B_TSAME%% ^>^> %%B_OSAME%%>> %TEMP_BAT%
252
echo if exist %%B_TDIFF%% type %%B_TDIFF%% ^>^> %%B_ODIFF%%>> %TEMP_BAT%
253
echo if exist %%B_TREDU%% type %%B_TREDU%% ^>^> %%B_OREDU%%>> %TEMP_BAT%
254
echo if exist %%B_TLOST%% type %%B_TLOST%% ^>^> %%B_OLOST%%>> %TEMP_BAT%
255
256
echo if exist %%B_TSAME%% type %%B_TSAME%% ^>^> %%B_ODIR%%>> %TEMP_BAT%
257
echo if exist %%B_TDIFF%% type %%B_TDIFF%% ^>^> %%B_ODIR%%>> %TEMP_BAT%
258
echo if exist %%B_TREDU%% type %%B_TREDU%% ^>^> %%B_ODIR%%>> %TEMP_BAT%
259
echo if exist %%B_TLOST%% type %%B_TLOST%% ^>^> %%B_ODIR%%>> %TEMP_BAT%
260
261
echo if exist %%B_TSAME%% del /q /f %%B_TSAME%% ^> nul>> %TEMP_BAT%
262
echo if exist %%B_TDIFF%% del /q /f %%B_TDIFF%% ^> nul>> %TEMP_BAT%
263
echo if exist %%B_TREDU%% del /q /f %%B_TREDU%% ^> nul>> %TEMP_BAT%
264
echo if exist %%B_TLOST%% del /q /f %%B_TLOST%% ^> nul>> %TEMP_BAT%
265
266
echo goto end>> %TEMP_BAT%
267
echo :out>> %TEMP_BAT%
268
echo echo %%*>> %TEMP_BAT%
269
echo goto :EOF>> %TEMP_BAT%
270
271
echo :end>> %TEMP_BAT%
272
rem ==========临时脚本生成完毕==========
273
274
set FN=
275
276
if "%O_MODI%" == "y" start "edit before run %TEMP_BAT%" /wait "%P_EDTR%" %TEMP_BAT%
277
cmd.exe /V:ON /C %TEMP_BAT%
278
279
set FN=
280
281
del /f /q %TEMP_BAT%
282
set TEMP_BAT=
283
284
if "%O_VIEW%"=="y" (
285
if exist %PWD%\%O_PRE%-diff.%O_POST% start "view" %PWD%\%O_PRE%-diff.%O_POST%
286
if exist %PWD%\%O_PRE%-same.%O_POST% start "view" %PWD%\%O_PRE%-same.%O_POST%
287
if exist %PWD%\%O_PRE%-lost.%O_POST% start "view" %PWD%\%O_PRE%-lost.%O_POST%
288
if exist %PWD%\%O_PRE%-redu.%O_POST% start "view" %PWD%\%O_PRE%-redu.%O_POST%
289
if exist %PWD%\%O_PRE%-all.%O_POST% start "view" %PWD%\%O_PRE%-all.%O_POST%
290
if exist %PWD%\%O_PRE%-dir.%O_POST% start "view" %PWD%\%O_PRE%-dir.%O_POST%
291
)
292
goto end
293
294
:end
295
set DEBUG=
296
set CMDLINE=
297
set PWD=
298
set DIR1=
299
set DIR2=
300
set DIR1S=
301
set DIR2S=
302
set RND=
303
304
set O_SUB=
305
set O_PAT=
306
set O_DIR=
307
set O_SUP=
308
set O_OVER=
309
set O_VIEW=
310
set O_PRE=
311
set O_CMDL=
312
set O_EXC=
313
set O_MODI=
314
set O_POST=
315
set O_QUITE=
316
set P_EDTR=
317
318
set SRC=
319
set DEST=
320
set B_1=
321
set B_2=
322
set B_DDIR=
323
set B_OALL=
324
set B_ODIFF=
325
set B_ODIR=
326
set B_OLOST=
327
set B_OREDU=
328
set B_OSAME=
329
set B_SDIR=
330
set B_TDIFF=
331
set B_TLOST=
332
set B_TREDU=
333
set B_TSAME=
334
1.比较两个目录的差异,支持目录的递归比较,判断标准是文件内容是否相同。
2.生成标准化的结果报告
小技巧:
1.如果加入-d参数,则会把所有的比较结果按照目录为单位输出到一个文件中。
2.对于*-diff.txt文件,可以用查找替换,把它变成一个bat文件,可以对有差异的文件用更高级的比较器进行比较。我经常会把diff.txt中的diff替换成call bc /w ,把 . 替换成rem,然后另存为bat格式。如果大家有要求,我考虑把它变成一个标准的输出格式
目前尚未实现的功能:
1. 除了文本之外的其他输出格式支持,比如xml或html的输出格式
下载代码如下:


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


公众号:老翅寒暑
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了