mm_os 3.3.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/LICENSE +21 -201
  2. package/README.md +491 -99
  3. package/README_EN.md +498 -0
  4. package/adapter/adapter.js +431 -0
  5. package/adapter/custom_persistence.js +660 -0
  6. package/adapter/mqtt.js +273 -0
  7. package/adapter/socket.js +113 -0
  8. package/adapter/web.js +67 -0
  9. package/adapter/websocket.js +146 -0
  10. package/com/api/com.json +5 -0
  11. package/{core/com → com}/api/config.tpl.json +8 -8
  12. package/com/api/drive.js +708 -0
  13. package/com/api/index.js +198 -0
  14. package/com/api/oauth.js +200 -0
  15. package/com/api/script.tpl.js +32 -0
  16. package/com/cmd/README.md +11 -0
  17. package/com/cmd/com.json +5 -0
  18. package/com/cmd/config.tpl.json +122 -0
  19. package/com/cmd/drive.js +1548 -0
  20. package/com/cmd/index.js +1066 -0
  21. package/com/cmd/msg.json +48 -0
  22. package/com/cmd/nlp.js +525 -0
  23. package/com/cmd/script.tpl.js +32 -0
  24. package/com/db/com.json +5 -0
  25. package/com/db/drive.js +1999 -0
  26. package/com/db/index.js +242 -0
  27. package/{core/com → com}/event/README.md +4 -4
  28. package/com/event/com.json +5 -0
  29. package/{core/com → com}/event/config.tpl.json +18 -18
  30. package/com/event/drive.js +59 -0
  31. package/com/event/index.js +409 -0
  32. package/com/event/script.tpl.js +23 -0
  33. package/com/mqtt/com.json +5 -0
  34. package/{core/com → com}/mqtt/config.tpl.json +3 -5
  35. package/com/mqtt/drive.js +676 -0
  36. package/com/mqtt/index.js +822 -0
  37. package/com/mqtt/mm_mqtt.js +425 -0
  38. package/com/mqtt/script.tpl.js +723 -0
  39. package/com/nav/com.json +5 -0
  40. package/com/nav/config.tpl.json +84 -0
  41. package/com/nav/drive.js +702 -0
  42. package/com/nav/index.js +231 -0
  43. package/{core/com → com}/nav/tpl/admin_pc/page_config.vue +280 -280
  44. package/{core/com → com}/nav/tpl/admin_pc/page_config_form.vue +194 -194
  45. package/com/nav/tpl/admin_pc/page_form.vue +180 -0
  46. package/com/nav/tpl/admin_pc/page_view.vue +124 -0
  47. package/com/nav/tpl/dev_pc/page_default.vue +247 -0
  48. package/com/nav/tpl/dev_pc/page_type.vue +313 -0
  49. package/com/nav/tpl/home_pc/page_default.vue +234 -0
  50. package/com/nav/tpl/home_pc/page_form.vue +137 -0
  51. package/com/nav/tpl/home_pc/page_list.vue +234 -0
  52. package/com/nav/tpl/home_pc/page_nav.vue +221 -0
  53. package/com/nav/tpl/home_pc/page_type.vue +234 -0
  54. package/com/nav/tpl/home_pc/page_view.vue +125 -0
  55. package/com/nav/tpl/home_phone/page_channel.vue +234 -0
  56. package/com/nav/tpl/home_phone/page_default.vue +234 -0
  57. package/com/nav/tpl/home_phone/page_form.vue +137 -0
  58. package/com/nav/tpl/home_phone/page_nav.vue +237 -0
  59. package/com/nav/tpl/home_phone/page_type.vue +234 -0
  60. package/com/nav/tpl/home_phone/page_view.vue +125 -0
  61. package/com/nav/viewmodel.js +446 -0
  62. package/com/param/com.json +5 -0
  63. package/{core/com → com}/param/config.tpl.json +7 -1
  64. package/com/param/drive.js +502 -0
  65. package/com/param/index.js +155 -0
  66. package/com/param/script.tpl.js +12 -0
  67. package/com/pendant/com.json +5 -0
  68. package/{core/com/component → com/pendant}/config.tpl.json +15 -13
  69. package/com/pendant/drive.js +204 -0
  70. package/com/pendant/index.js +441 -0
  71. package/com/pendant/pendant.html +16 -0
  72. package/com/pendant/script.tpl.js +18 -0
  73. package/com/socket/com.json +5 -0
  74. package/com/socket/config.tpl.json +12 -0
  75. package/com/socket/drive.js +651 -0
  76. package/com/socket/index.js +351 -0
  77. package/com/socket/script.tpl.js +41 -0
  78. package/com/sql/com.json +5 -0
  79. package/{core/com → com}/sql/config.tpl.json +13 -9
  80. package/com/sql/drive.js +1259 -0
  81. package/com/sql/index.js +150 -0
  82. package/com/sql/script.tpl.js +47 -0
  83. package/com/static/com.json +5 -0
  84. package/{core/com → com}/static/config.tpl.json +10 -6
  85. package/com/static/drive.js +194 -0
  86. package/com/static/index.js +226 -0
  87. package/com/static/script.tpl.js +28 -0
  88. package/com/task/com.json +5 -0
  89. package/{core/com → com}/task/config.tpl.json +4 -6
  90. package/com/task/drive.js +405 -0
  91. package/com/task/index.js +148 -0
  92. package/com/task/script.tpl.js +37 -0
  93. package/com/template/com.json +5 -0
  94. package/com/template/config.tpl.json +16 -0
  95. package/com/template/drive.js +80 -0
  96. package/com/template/index.js +141 -0
  97. package/com.js +156 -0
  98. package/common/README.md +2 -0
  99. package/common/handler/msg/handler.json +22 -0
  100. package/common/handler/msg/index.js +23 -0
  101. package/common/handler/player/handler.json +22 -0
  102. package/common/handler/player/index.js +287 -0
  103. package/common/handler/user/handler.json +22 -0
  104. package/common/handler/user/index.js +23 -0
  105. package/common/middleware/web_after/index.js +29 -0
  106. package/common/middleware/web_after/middleware.json +9 -0
  107. package/common/middleware/web_base/index.js +113 -0
  108. package/common/middleware/web_base/middleware.json +19 -0
  109. package/common/middleware/web_before/index.js +33 -0
  110. package/common/middleware/web_before/middleware.json +9 -0
  111. package/common/middleware/web_cors/index.js +87 -0
  112. package/common/middleware/web_cors/middleware.json +24 -0
  113. package/common/middleware/web_error/index.js +119 -0
  114. package/common/middleware/web_error/middleware.json +18 -0
  115. package/common/middleware/web_ip/index.js +15 -0
  116. package/common/middleware/web_ip/middleware.json +14 -0
  117. package/common/middleware/web_logger/index.js +156 -0
  118. package/common/middleware/web_logger/middleware.json +14 -0
  119. package/common/middleware/web_main/index.js +24 -0
  120. package/common/middleware/web_main/middleware.json +9 -0
  121. package/common/middleware/web_static/index.js +73 -0
  122. package/common/middleware/web_static/middleware.json +54 -0
  123. package/common/middleware/web_waf/index.js +385 -0
  124. package/common/middleware/web_waf/middleware.json +13 -0
  125. package/common/model/msg/index.js +88 -0
  126. package/common/model/msg/model.json +401 -0
  127. package/common/model/player/index.js +63 -0
  128. package/common/model/player/model.json +185 -0
  129. package/common/model/user/index.js +11 -0
  130. package/common/model/user/model.json +219 -0
  131. package/core/app/config.tpl.json +67 -0
  132. package/core/app/index.js +632 -0
  133. package/core/app/script.tpl.js +52 -0
  134. package/core/channel/index.js +899 -0
  135. package/core/channel/matcher.js +585 -0
  136. package/core/com/config.tpl.json +16 -0
  137. package/core/com/index.js +74 -0
  138. package/core/com/script.tpl.js +5 -0
  139. package/core/component/component.js +42 -0
  140. package/core/component/config.tpl.json +63 -0
  141. package/core/component/index.js +273 -0
  142. package/core/component/script.tpl.js +19 -0
  143. package/core/controller/config.tpl.json +14 -0
  144. package/core/controller/index.js +373 -0
  145. package/core/controller/script.tpl.js +27 -0
  146. package/core/factory/config.tpl.json +14 -0
  147. package/core/factory/entity.js +275 -0
  148. package/core/factory/index.js +241 -0
  149. package/core/factory/script.tpl.js +16 -0
  150. package/core/game/bat/index.js +137 -0
  151. package/core/game/bat/world.js +622 -0
  152. package/core/game/config.tpl.json +16 -0
  153. package/core/game/entity_admin.js +230 -0
  154. package/core/game/index.js +186 -0
  155. package/core/handler/config.tpl.json +22 -0
  156. package/core/handler/index.js +181 -0
  157. package/core/handler/script.tpl.js +23 -0
  158. package/core/logic/config.tpl.json +14 -0
  159. package/core/logic/index.js +59 -0
  160. package/core/logic/script.tpl.js +19 -0
  161. package/core/middleware/config.tpl.json +16 -0
  162. package/core/middleware/index.js +125 -0
  163. package/core/middleware/script.tpl.js +37 -0
  164. package/core/mod/config.tpl.json +22 -0
  165. package/core/mod/index.js +130 -0
  166. package/core/mod/script.tpl.js +34 -0
  167. package/core/model/config.tpl.json +219 -0
  168. package/core/model/index.js +272 -0
  169. package/core/model/model.js +27 -0
  170. package/core/model/script.tpl.js +20 -0
  171. package/core/notifier/config.tpl.json +14 -0
  172. package/core/notifier/index.js +77 -0
  173. package/core/notifier/script.tpl.js +20 -0
  174. package/core/plugin/config.tpl.json +24 -0
  175. package/core/plugin/index.js +232 -0
  176. package/core/plugin/script.tpl.js +51 -0
  177. package/core/pusher/config.tpl.json +14 -0
  178. package/core/pusher/index.js +161 -0
  179. package/core/pusher/script.tpl.js +20 -0
  180. package/core/room/bat/index.js +170 -0
  181. package/core/room/bat/room.js +524 -0
  182. package/core/room/config.tpl.json +20 -0
  183. package/core/room/index.js +249 -0
  184. package/core/room/room.js +61 -0
  185. package/core/scene/config.tpl.json +14 -0
  186. package/core/scene/index.js +466 -0
  187. package/core/scene/loop.js +1255 -0
  188. package/core/scene/map.js +28 -0
  189. package/core/scene/script.tpl.js +22 -0
  190. package/core/sender/config.tpl.json +14 -0
  191. package/core/sender/index.js +79 -0
  192. package/core/sender/script.tpl.js +20 -0
  193. package/core/service/config.tpl.json +14 -0
  194. package/core/service/index.js +100 -0
  195. package/core/service/script.tpl.js +25 -0
  196. package/core/store/config.tpl.json +26 -0
  197. package/core/store/index.js +1755 -0
  198. package/core/store/script.tpl.js +22 -0
  199. package/core/store/sql.js +1464 -0
  200. package/core/system/config.tpl.json +18 -0
  201. package/core/system/index.js +312 -0
  202. package/core/system/script.tpl.js +77 -0
  203. package/core/view/config.tpl.json +14 -0
  204. package/core/view/index.js +91 -0
  205. package/core/view/script.tpl.js +20 -0
  206. package/core/zone/bat/index.js +725 -0
  207. package/core/zone/config.tpl.json +54 -0
  208. package/core/zone/index.js +614 -0
  209. package/core/zone/script.tpl.js +10 -0
  210. package/core/zone/zone_bat.js +136 -0
  211. package/core//345/237/272/347/261/273/346/250/241/345/235/227/346/270/205/345/215/225.md +24 -0
  212. package/index.js +17 -333
  213. package/os.js +57 -0
  214. package/package.json +58 -58
  215. package/server.js +598 -0
  216. package/README.en.md +0 -36
  217. package/conf.json +0 -3
  218. package/core/base/mqtt/index.js +0 -1110
  219. package/core/base/mqtt/lib.js +0 -40
  220. package/core/base/web/index.js +0 -245
  221. package/core/com/api/com.json +0 -4
  222. package/core/com/api/drive.js +0 -668
  223. package/core/com/api/index.js +0 -108
  224. package/core/com/api/oauth.js +0 -158
  225. package/core/com/api/script.js +0 -32
  226. package/core/com/app/README.md +0 -3
  227. package/core/com/app/com.json +0 -4
  228. package/core/com/app/config.tpl.json +0 -16
  229. package/core/com/app/drive.js +0 -309
  230. package/core/com/app/index.js +0 -211
  231. package/core/com/app/script.js +0 -155
  232. package/core/com/cmd/com.json +0 -4
  233. package/core/com/cmd/config.tpl.json +0 -66
  234. package/core/com/cmd/drive.js +0 -513
  235. package/core/com/cmd/index.js +0 -354
  236. package/core/com/cmd/old/5w2h.js +0 -54
  237. package/core/com/cmd/old/drive.js +0 -423
  238. package/core/com/cmd/script.js +0 -11
  239. package/core/com/component/README.md +0 -3
  240. package/core/com/component/com.json +0 -4
  241. package/core/com/component/component.html +0 -16
  242. package/core/com/component/drive.js +0 -197
  243. package/core/com/component/index.js +0 -312
  244. package/core/com/component/script.js +0 -18
  245. package/core/com/db/com.json +0 -4
  246. package/core/com/db/drive.js +0 -1160
  247. package/core/com/db/index.js +0 -176
  248. package/core/com/event/com.json +0 -4
  249. package/core/com/event/drive.js +0 -133
  250. package/core/com/event/index.js +0 -345
  251. package/core/com/event/script.js +0 -26
  252. package/core/com/eventer/com.js +0 -477
  253. package/core/com/eventer/com.json +0 -4
  254. package/core/com/middleware/com.js +0 -154
  255. package/core/com/middleware/com.json +0 -4
  256. package/core/com/middleware/config.tpl.json +0 -8
  257. package/core/com/middleware/script.js +0 -9
  258. package/core/com/mqtt/com.json +0 -4
  259. package/core/com/mqtt/drive.js +0 -600
  260. package/core/com/mqtt/index.js +0 -572
  261. package/core/com/mqtt/mm_mqtt.js +0 -330
  262. package/core/com/mqtt/script.js +0 -604
  263. package/core/com/msg/com.js +0 -296
  264. package/core/com/msg/com.json +0 -4
  265. package/core/com/nav/com.json +0 -4
  266. package/core/com/nav/config.tpl.json +0 -75
  267. package/core/com/nav/drive.js +0 -549
  268. package/core/com/nav/index.js +0 -182
  269. package/core/com/nav/tpl/admin_pc/page_form.vue +0 -180
  270. package/core/com/nav/tpl/admin_pc/page_view.vue +0 -124
  271. package/core/com/nav/tpl/dev_pc/page_default.vue +0 -247
  272. package/core/com/nav/tpl/dev_pc/page_type.vue +0 -313
  273. package/core/com/nav/tpl/home_pc/page_default.vue +0 -234
  274. package/core/com/nav/tpl/home_pc/page_form.vue +0 -137
  275. package/core/com/nav/tpl/home_pc/page_list.vue +0 -234
  276. package/core/com/nav/tpl/home_pc/page_nav.vue +0 -221
  277. package/core/com/nav/tpl/home_pc/page_type.vue +0 -234
  278. package/core/com/nav/tpl/home_pc/page_view.vue +0 -125
  279. package/core/com/nav/tpl/home_phone/page_channel.vue +0 -234
  280. package/core/com/nav/tpl/home_phone/page_default.vue +0 -234
  281. package/core/com/nav/tpl/home_phone/page_form.vue +0 -137
  282. package/core/com/nav/tpl/home_phone/page_nav.vue +0 -237
  283. package/core/com/nav/tpl/home_phone/page_type.vue +0 -234
  284. package/core/com/nav/tpl/home_phone/page_view.vue +0 -125
  285. package/core/com/nav/viewmodel.js +0 -296
  286. package/core/com/param/drive.js +0 -366
  287. package/core/com/param/index.js +0 -80
  288. package/core/com/param/script.js +0 -12
  289. package/core/com/param/test.js +0 -98
  290. package/core/com/plugin/README.md +0 -3
  291. package/core/com/plugin/com.json +0 -4
  292. package/core/com/plugin/config.tpl.json +0 -26
  293. package/core/com/plugin/drive.js +0 -536
  294. package/core/com/plugin/index.js +0 -259
  295. package/core/com/plugin/script.js +0 -213
  296. package/core/com/rpc/com.json +0 -4
  297. package/core/com/rpc/drive.js +0 -160
  298. package/core/com/rpc/index.js +0 -87
  299. package/core/com/rpc/rpc.js +0 -118
  300. package/core/com/socket/com.json +0 -4
  301. package/core/com/socket/config.tpl.json +0 -14
  302. package/core/com/socket/drive.js +0 -403
  303. package/core/com/socket/index.js +0 -62
  304. package/core/com/socket/script.js +0 -42
  305. package/core/com/sql/drive.js +0 -1087
  306. package/core/com/sql/index.js +0 -83
  307. package/core/com/sql/script.js +0 -48
  308. package/core/com/static/com.json +0 -4
  309. package/core/com/static/drive.js +0 -220
  310. package/core/com/static/index.js +0 -149
  311. package/core/com/static/script.js +0 -28
  312. package/core/com/task/com.json +0 -4
  313. package/core/com/task/drive.js +0 -403
  314. package/core/com/task/index.js +0 -110
  315. package/core/com/task/script.js +0 -37
  316. package/core/com/timer/com.js +0 -217
  317. package/core/com/timer/com.json +0 -4
  318. package/core/com/tpl/com.js +0 -19
  319. package/core/com/tpl/com.json +0 -4
  320. package/lib/actions.js +0 -50
  321. package/lib/base.js +0 -361
  322. package/lib/com.js +0 -29
  323. package/lib/ref.js +0 -121
  324. package/middleware/cors/index.js +0 -119
  325. package/middleware/cors/middleware.json +0 -20
  326. package/middleware/csrf/index.js +0 -202
  327. package/middleware/csrf/middleware.json +0 -24
  328. package/middleware/ip_firewall/index.js +0 -476
  329. package/middleware/ip_firewall/middleware.json +0 -109
  330. package/middleware/mqtt_base/index.js +0 -10
  331. package/middleware/mqtt_base/middleware.json +0 -11
  332. package/middleware/security_audit/index.js +0 -543
  333. package/middleware/security_audit/middleware.json +0 -48
  334. package/middleware/waf/index.js +0 -343
  335. package/middleware/waf/middleware.json +0 -10
  336. package/middleware/waf_ddos/index.js +0 -520
  337. package/middleware/waf_ddos/middleware.json +0 -38
  338. package/middleware/waf_xss/index.js +0 -269
  339. package/middleware/waf_xss/middleware.json +0 -18
  340. package/middleware/web_after/index.js +0 -33
  341. package/middleware/web_after/middleware.json +0 -10
  342. package/middleware/web_base/index.js +0 -90
  343. package/middleware/web_base/middleware.json +0 -10
  344. package/middleware/web_before/index.js +0 -27
  345. package/middleware/web_before/middleware.json +0 -10
  346. package/middleware/web_check/index.js +0 -28
  347. package/middleware/web_check/middleware.json +0 -10
  348. package/middleware/web_main/index.js +0 -28
  349. package/middleware/web_main/middleware.json +0 -10
  350. package/middleware/web_proxy/index.js +0 -37
  351. package/middleware/web_proxy/middleware.json +0 -10
  352. package/middleware/web_render/index.js +0 -87
  353. package/middleware/web_render/middleware.json +0 -10
  354. package/middleware/web_socket/index.js +0 -34
  355. package/middleware/web_socket/middleware.json +0 -10
  356. package/middleware/web_static/index.js +0 -115
  357. package/middleware/web_static/middleware.json +0 -10
  358. /package/{core/com → com}/api/README.md +0 -0
  359. /package/{core/com → com}/db/README.md +0 -0
  360. /package/{core/com → com}/mqtt/README.md +0 -0
  361. /package/{core/com → com}/nav/README.md +0 -0
  362. /package/{core/com → com}/nav/tpl/admin_pc/page_channel.vue +0 -0
  363. /package/{core/com → com}/nav/tpl/admin_pc/page_default.vue +0 -0
  364. /package/{core/com → com}/nav/tpl/admin_pc/page_lang.vue +0 -0
  365. /package/{core/com → com}/nav/tpl/admin_pc/page_nav.vue +0 -0
  366. /package/{core/com → com}/nav/tpl/admin_pc/page_table.vue +0 -0
  367. /package/{core/com → com}/nav/tpl/admin_pc/page_type.vue +0 -0
  368. /package/{core/com → com}/nav/tpl/dev_pc/page_channel.vue +0 -0
  369. /package/{core/com → com}/nav/tpl/dev_pc/page_config.vue +0 -0
  370. /package/{core/com → com}/nav/tpl/dev_pc/page_form.vue +0 -0
  371. /package/{core/com → com}/nav/tpl/dev_pc/page_nav.vue +0 -0
  372. /package/{core/com → com}/nav/tpl/dev_pc/page_table.vue +0 -0
  373. /package/{core/com → com}/nav/tpl/home_pc/page_channel.vue +0 -0
  374. /package/{core/com → com}/nav/tpl/home_phone/page_list.vue +0 -0
  375. /package/{core/com → com}/param/README.md +0 -0
  376. /package/{core/com/cmd → com/pendant}/README.md +0 -0
  377. /package/{core/com → com}/socket/README.md +0 -0
  378. /package/{core/com → com}/sql/README.md +0 -0
  379. /package/{core/com → com}/static/README.md +0 -0
  380. /package/{core/com → com}/task/README.md +0 -0
@@ -0,0 +1,1755 @@
1
+ const {
2
+ Drive
3
+ } = require('mm_machine');
4
+ const { idGen } = require('../../ulits/id_gen.js');
5
+ const Sql = require('./sql.js');
6
+
7
+ /**
8
+ * 数据存储基类
9
+ * 提供数据访问层的基础功能和CRUD操作接口,调用缓存和持久存储
10
+ */
11
+ class Store extends Drive {
12
+ /**
13
+ * 验证配置参数
14
+ * @param {object} config 配置参数
15
+ * @returns {boolean} 配置是否有效
16
+ */
17
+ static validateConfig(config) {
18
+ if (!config || typeof config !== 'object') {
19
+ throw new TypeError('配置参数必须是对象');
20
+ }
21
+
22
+ Store._validateReqFields(config);
23
+ Store._validateCacheType(config);
24
+ Store._validateSaveType(config);
25
+ Store._validateWay(config);
26
+ Store._validateNumFields(config);
27
+
28
+ return true;
29
+ }
30
+
31
+ /**
32
+ * 验证必需字段
33
+ * @param {object} config 配置参数
34
+ */
35
+ static _validateReqFields(config) {
36
+ var required_fields = ['name', 'cache_type', 'save_type', 'way', 'table'];
37
+ for (var field of required_fields) {
38
+ if (!config[field]) {
39
+ throw new Error(`配置参数缺少必要字段: ${field}`);
40
+ }
41
+ }
42
+ }
43
+
44
+ /**
45
+ * 验证缓存类型
46
+ * @param {object} config 配置参数
47
+ */
48
+ static _validateCacheType(config) {
49
+ var valid_cache_types = ['none', 'memory', 'redis', 'mongodb'];
50
+ if (!valid_cache_types.includes(config.cache_type)) {
51
+ throw new Error(`无效的缓存类型: ${config.cache_type}`);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 验证保存类型
57
+ * @param {object} config 配置参数
58
+ */
59
+ static _validateSaveType(config) {
60
+ var valid_save_types = ['none', 'sqlite', 'mysql', 'json'];
61
+ if (!valid_save_types.includes(config.save_type)) {
62
+ throw new Error(`无效的保存类型: ${config.save_type}`);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 验证读写方式
68
+ * @param {object} config 配置参数
69
+ */
70
+ static _validateWay(config) {
71
+ if (config.way < 1 || config.way > 4) {
72
+ throw new Error(`无效的读写方式: ${config.way}`);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * 验证数值类型参数
78
+ * @param {object} config 配置参数
79
+ */
80
+ static _validateNumFields(config) {
81
+ var numeric_fields = ['cache_interval', 'save_interval', 'page_size'];
82
+ for (var num_field of numeric_fields) {
83
+ if (config[num_field] && (typeof config[num_field] !== 'number' || config[num_field] < 0)) {
84
+ throw new Error(`${num_field} 必须是正数`);
85
+ }
86
+ }
87
+ }
88
+
89
+ static config = {
90
+ // 名称
91
+ 'name': 'default',
92
+ // 标题
93
+ 'title': '示例标题',
94
+ // 描述
95
+ 'description': '示例描述',
96
+ // 入口文件
97
+ 'main': '',
98
+ // 作用域,决定加载到什么区域
99
+ 'scope': 'server',
100
+ // 状态 0:禁用 1:启用
101
+ 'state': 1,
102
+ // 排序,越小越靠前
103
+ 'sort': 100,
104
+ // 缓存方式 none、memory、redis、mongodb
105
+ 'cache_type': 'none',
106
+ // 保存方式 none、sqlite、mysql、json
107
+ 'save_type': 'none',
108
+ // 缓存更新间隔(秒)
109
+ 'cache_interval': 600, // 默认10分钟
110
+ // 定时持久化间隔(秒)
111
+ 'save_interval': 300, // 默认5分钟
112
+ // 读写方式 1.直接读写数据库;2.读缓存,写持久存,写后更新缓存;3.读缓存,写缓存,定时持久存;4.读缓存,写持久存,定时更新缓存
113
+ 'way': 1,
114
+ // 表名
115
+ 'table': 'store',
116
+ // 主键
117
+ 'key': 'id',
118
+ // ID
119
+ 'id': 'id',
120
+ // 默认分页大小
121
+ 'page_size': 30,
122
+ // 排序方式,{0}表示支持前端传值来排序,传值方式例如:orderby=user_id
123
+ 'orderby': '{0}',
124
+ // 默认排序方式,当前端没有传值的情况下,采用默认传值方式
125
+ 'orderby_default': '`user_id` desc',
126
+ // 查询列表时返回的字段,{0}表示支持前端传值
127
+ 'field': '{0}',
128
+ // 查询对象时返回的字段
129
+ 'field_obj': '{0}',
130
+ // 默认返回字段
131
+ 'field_default': '`username`, `phone`',
132
+ // 需要过滤的字段,*代表泛匹配 前面*表示以xxx结尾,后面*表示以xxx开头,前后*表示包含xxx的过滤
133
+ 'field_hide': ['*password*', '*token*'],
134
+ // 传值分隔符,如果允许多个值,可以通过这个来分割,如果不允许传多个值则设置为空
135
+ 'separator': '|',
136
+ // 过滤参数
137
+ 'filter': {
138
+ /**
139
+ * 表名
140
+ */
141
+ 'table': 'table',
142
+ /**
143
+ * 查询的页码
144
+ */
145
+ 'page': 'page',
146
+ /**
147
+ * 查询每页条数
148
+ */
149
+ 'size': 'size',
150
+ /**
151
+ * 操作方式: 传入参数method=add, 支持参数 add增、del删、set改、get查,为空则为get
152
+ */
153
+ 'method': 'method',
154
+ /**
155
+ * 排序
156
+ */
157
+ 'orderby': 'orderby',
158
+ /**
159
+ * 查询显示的字段
160
+ */
161
+ 'field': 'field',
162
+ /**
163
+ * 统计结果: 统计符合条件的结果数,只有当page等于1或0时才会统计
164
+ */
165
+ 'count_ret': 'count_ret'
166
+ },
167
+ // 允许调用的方法 add增 del删 set改 get查 get_obj查一条 import导入 export导出, 支持什么方式就传什么值,支持多个用空格分隔
168
+ 'method': 'add del set get get_obj import export del_repeat avg sum count update',
169
+ // sql查询语句
170
+ 'query': {
171
+ // 当username=xx时,转为`username` like '%xxx%'的SQL语法
172
+ // 'username': '`username` like \'%{0}\''
173
+ },
174
+ // 默认查询, 当查询条件中不包含该项时,默认添加该项
175
+ 'query_default': {},
176
+ // sql更改语句
177
+ 'update': {
178
+ // 当修改的正文中出现username是转换为指定格式
179
+ // 'username': '`username` = \'{0}\''
180
+ },
181
+ // 默认添加条件,当不包含该项时,默认添加该项
182
+ 'body_default': {},
183
+ // 逻辑符
184
+ 'logic': {},
185
+ // 输出sql语句
186
+ 'log': false,
187
+ // 文件路径, 当调用函数不存在时,会先从文件中加载
188
+ 'main': '',
189
+ // 回调函数名 用于决定调用脚本的哪个函数
190
+ 'func_name': '',
191
+ // 参数 []
192
+ 'params': null,
193
+ // 导入导出格式,转换导入、导出Eexcel时字段名和字段值
194
+ 'format': [],
195
+ // 去重条件,需要查重、并去重的字段在这里写
196
+ 'del_repeat': {
197
+ // 判断重复的字段
198
+ 'groupBy': '',
199
+ // 排序方式
200
+ 'orderBy': ''
201
+ }
202
+ };
203
+
204
+ /**
205
+ * 构造函数
206
+ * @param {object} config 配置参数
207
+ * @param {object} parent 父对象
208
+ */
209
+ constructor(config, parent) {
210
+ // 验证配置参数
211
+ Store.validateConfig({ ...Store.config, ...config });
212
+
213
+ super({ type: 'store', ...Store.config, ...config }, parent);
214
+ }
215
+ }
216
+
217
+ /**
218
+ * 预设
219
+ */
220
+ Store.prototype._preset = function () {
221
+ // 数据库管理器
222
+ this._sql = null;
223
+ // 缓存管理器
224
+ this._cache = null;
225
+ // 持久存定时器
226
+ this._save_timer = null;
227
+ // 缓存定时器
228
+ this._cache_timer = null;
229
+
230
+ this._cache_interval = this.config.cache_interval || 600; // 默认10分钟
231
+ this._save_interval = this.config.save_interval || 300; // 默认5分钟
232
+ };
233
+
234
+ /**
235
+ * 获取模板目录
236
+ * @returns {string} 模板目录
237
+ */
238
+ Store.prototype.getTplDir = function () {
239
+ return __dirname;
240
+ };
241
+
242
+ /**
243
+ * 初始化核心
244
+ * @param {object} db 数据库管理器
245
+ * @param {object} cache 缓存管理器
246
+ * @param {object} logger 日志管理器
247
+ */
248
+ Store.prototype._initCore = async function (db, cache, logger) {
249
+ // 初始化依赖项
250
+ if (logger) {
251
+ this.setLogger(logger);
252
+ }
253
+ if (db) {
254
+ this._db = db;
255
+ }
256
+ if (cache) {
257
+ this._cache = cache;
258
+ }
259
+ // 驱动实例
260
+ this._drive = new Sql(this.config);
261
+ // 初始化驱动
262
+ this._drive.sql = this._sql;
263
+ this._drive.cache = cache;
264
+
265
+ // 根据读写方式启动定时任务
266
+ this._startTimers();
267
+ };
268
+
269
+ /**
270
+ * 添加数据到数据库
271
+ * @param {object} body - 要添加的数据对象
272
+ * @returns {Promise<object>} - 添加结果
273
+ */
274
+ Store.prototype._addToDb = async function (body) {
275
+ let db = this._sql.db();
276
+ return await this._drive.add(db, body);
277
+ };
278
+
279
+ /**
280
+ * 添加数据到缓存
281
+ * @param {object} body - 要添加的数据对象
282
+ * @returns {Promise<object>} - 添加结果
283
+ */
284
+ Store.prototype._addToCache = async function (body) {
285
+ // 生成临时ID或使用现有ID
286
+ let temp = body[this.config.id] || `temp_${Date.now()}`;
287
+ let cached = {
288
+ ...body,
289
+ [this.config.id]: temp
290
+ };
291
+
292
+ // 保存到缓存
293
+ let cache_key = this._genCacheKey('get', {});
294
+ let cache_obj_key = this._genCacheKey('getObj', { [this.config.id]: temp });
295
+
296
+ // 获取当前列表缓存
297
+ let current = await this.getCache(cache_key) || [];
298
+ current.push(cached);
299
+
300
+ // 更新缓存
301
+ await this.setCache(cache_key, current);
302
+ await this.setCache(cache_obj_key, cached);
303
+
304
+ return cached;
305
+ };
306
+
307
+ /**
308
+ * 添加单条数据
309
+ * @param {object} body - 要添加的数据对象
310
+ * @returns {Promise<object>} - 添加成功后的数据对象
311
+ */
312
+ Store.prototype.add = async function (body) {
313
+ // 入参校验
314
+ if (typeof body !== 'object' || body === null) {
315
+ throw new TypeError('添加的数据必须是对象');
316
+ }
317
+ if (Object.keys(body).length === 0) {
318
+ throw new TypeError('添加的数据不能为空');
319
+ }
320
+
321
+ try {
322
+ let way = this.config.way;
323
+ return await this._addByWay(body, way);
324
+ } catch (error) {
325
+ this.log('error', '创建数据失败:', error);
326
+ return $.ret.error(error.code || 500, '创建数据失败');
327
+ }
328
+ };
329
+
330
+ /**
331
+ * 根据方式添加数据
332
+ * @param {object} body - 要添加的数据对象
333
+ * @param {number} way - 存储方式
334
+ * @returns {Promise<object>} - 添加结果
335
+ * @private
336
+ */
337
+ Store.prototype._addByWay = async function (body, way) {
338
+ let result;
339
+
340
+ // 方式1、2、4、2、4:写数据库
341
+ if ([1, 2, 4].includes(way)) {
342
+ result = await this._addToDb(body);
343
+ }
344
+
345
+ // 方式3:写缓存
346
+ if (way === 3 && this._cache) {
347
+ result = await this._addToCache(body);
348
+ }
349
+
350
+ // 方式2:写后更新缓存
351
+ if (way === 2 && this._cache) {
352
+ await this._updateCacheForWay2(result);
353
+ }
354
+
355
+ return result;
356
+ };
357
+
358
+ /**
359
+ * 批量添加数据到数据库
360
+ * @param {Array} list - 要添加的数据对象数组
361
+ * @returns {Promise<object>} - 添加结果
362
+ */
363
+ Store.prototype._addListToDb = async function (list) {
364
+ let db = this._sql.db();
365
+ return await this._drive.addList(db, list);
366
+ };
367
+
368
+ /**
369
+ * 批量添加数据到缓存
370
+ * @param {Array} list - 要添加的数据对象数组
371
+ * @returns {Promise<object>} - 添加结果
372
+ */
373
+ Store.prototype._addListToCache = async function (list) {
374
+ let cache_key = this._genCacheKey('get', {});
375
+ let current = await this.getCache(cache_key) || [];
376
+
377
+ // 为每条数据生成临时ID
378
+ let cached = list.map(item => {
379
+ let temp = item[this.config.id] || idGen.genId('temp');
380
+ let data = {
381
+ ...item,
382
+ [this.config.id]: temp
383
+ };
384
+
385
+ // 设置单条数据缓存
386
+ let cache_obj_key = this._genCacheKey('getObj', { [this.config.id]: temp });
387
+ this.setCache(cache_obj_key, data);
388
+
389
+ return data;
390
+ });
391
+
392
+ // 更新列表缓存
393
+ current.push(...cached);
394
+ await this.setCache(cache_key, current);
395
+
396
+ return {
397
+ result: {
398
+ success: list.length,
399
+ error: 0,
400
+ total: list.length,
401
+ errors: []
402
+ }
403
+ };
404
+ };
405
+
406
+ /**
407
+ * 批量添加数据
408
+ * @param {Array} list - 要添加的数据对象数组
409
+ * @returns {Promise<object>} - 添加结果
410
+ */
411
+ Store.prototype.addList = async function (list) {
412
+ // 入参校验
413
+ if (!Array.isArray(list)) {
414
+ throw new TypeError('批量添加的数据必须是数组');
415
+ }
416
+ if (list.length === 0) {
417
+ throw new TypeError('批量添加的数据不能为空');
418
+ }
419
+
420
+ try {
421
+ let way = this.config.way;
422
+ let result;
423
+
424
+ // 方式1、2、4、2、4:写数据库
425
+ if ([1, 2, 4].includes(way)) {
426
+ result = await this._addListToDb(list);
427
+ }
428
+
429
+ // 方式3:写缓存
430
+ if (way === 3 && this._cache) {
431
+ result = await this._addListToCache(list);
432
+ }
433
+
434
+ // 方式2:写后更新缓存
435
+ if (way === 2 && this._cache && result.result) {
436
+ // 清除列表缓存,下次读取时会自动更新
437
+ let cache_key = this._genCacheKey('get', {});
438
+ await this.delCache(cache_key);
439
+ }
440
+
441
+ return this._formatAddListRet(result, list.length);
442
+ } catch (error) {
443
+ this.log('error', '批量创建数据失败:', error);
444
+ return { error: true, message: '批量创建数据失败' };
445
+ }
446
+ };
447
+
448
+ /**
449
+ * 格式化批量添加结果
450
+ * @param {object} result - 原始结果
451
+ * @param {number} list_length - 列表长度
452
+ * @returns {object} - 格式化后的结果
453
+ */
454
+ Store.prototype._formatAddListRet = function (result, list_length) {
455
+ if (result.error) {
456
+ return {
457
+ success: 0,
458
+ error: list_length,
459
+ total: list_length,
460
+ errors: [result.message]
461
+ };
462
+ } else {
463
+ return {
464
+ success: result.result.success,
465
+ error: result.result.error,
466
+ total: result.result.total,
467
+ errors: result.result.errors
468
+ };
469
+ }
470
+ };
471
+
472
+ /**
473
+ * 尝试从缓存读取单条数据
474
+ * @param {string} cache_key - 缓存键
475
+ * @param {number} way - 读写方式
476
+ * @returns {Promise<object|null>} - 缓存数据或null
477
+ */
478
+ Store.prototype._tryGetFromCache = async function (cache_key, way) {
479
+ if ([2, 3, 4].includes(way) && this._cache) {
480
+ let cached = await this.getCache(cache_key);
481
+ if (cached) {
482
+ return cached;
483
+ }
484
+ }
485
+ return null;
486
+ };
487
+
488
+ /**
489
+ * 从数据库读取单条数据
490
+ * @param {object} query - 查询条件
491
+ * @returns {Promise<object|null>} - 查询结果
492
+ */
493
+ Store.prototype._getFromDb = async function (query) {
494
+ let db = this._sql.db();
495
+ db.table = this.config.table;
496
+
497
+ let result = await this._drive.getObj(db, query);
498
+ return result.result ? result.result : null;
499
+ };
500
+
501
+ /**
502
+ * 查询单条数据
503
+ * @param {object} query - 查询条件
504
+ * @returns {Promise<object|null>} - 查询到的数据对象或null
505
+ */
506
+ Store.prototype.getObj = async function (query = {}) {
507
+ // 入参校验
508
+ if (typeof query !== 'object' || query === null) {
509
+ throw new TypeError('查询条件必须是对象');
510
+ }
511
+
512
+ let way = this.config.way;
513
+ let cache_key = this._genCacheKey('getObj', query);
514
+
515
+ // 尝试从缓存读取
516
+ let cached = await this._tryGetFromCache(cache_key, way);
517
+ if (cached) {
518
+ return cached;
519
+ }
520
+
521
+ // 从数据库读取
522
+ let data = await this._getFromDb(query);
523
+
524
+ // 更新缓存(方式2)
525
+ await this._updateCache2Get(cache_key, way, data);
526
+
527
+ return data;
528
+ };
529
+
530
+ /**
531
+ * 查询多条数据
532
+ * @param {object} query - 查询条件
533
+ * @returns {Promise<Array>} - 查询结果数组
534
+ */
535
+ Store.prototype.get = async function (query = {}) {
536
+ // 入参校验
537
+ if (typeof query !== 'object' || query === null) {
538
+ throw new TypeError('查询条件必须是对象');
539
+ }
540
+
541
+ return await this._getCache(query);
542
+ };
543
+
544
+ /**
545
+ * 带缓存的查询实现
546
+ * @private
547
+ * @param {object} query - 查询条件
548
+ * @returns {Promise<Array>} - 查询结果数组
549
+ */
550
+ Store.prototype._getCache = async function (query) {
551
+ let way = this.config.way;
552
+ let cache_key = this._genCacheKey('get', query);
553
+
554
+ // 尝试从缓存读取
555
+ let cached = await this._tryGetListFromCache(cache_key, way);
556
+ if (cached) {
557
+ return cached;
558
+ }
559
+
560
+ // 从数据库读取
561
+ let data = await this._getListFromDb(query);
562
+
563
+ // 更新缓存(方式2)
564
+ await this._updateCache2List(cache_key, way, data);
565
+
566
+ return data;
567
+ };
568
+
569
+
570
+
571
+ /**
572
+ * 根据ID查询数据
573
+ * @param {string|number} id - 数据ID
574
+ * @param {string} orderby - 排序字段
575
+ * @param {string} fields - 查询字段
576
+ * @returns {Promise<object|null>} - 查询到的数据对象或null
577
+ */
578
+ Store.prototype.getById = async function (id, orderby = '', fields = '') {
579
+ // 入参校验
580
+ if (!id && id !== 0) {
581
+ throw new TypeError('ID不能为空');
582
+ }
583
+
584
+ return await this._getByIdCache(id, orderby, fields);
585
+ };
586
+
587
+ /**
588
+ * 带缓存的根据ID查询实现
589
+ * @private
590
+ * @param {string|number} id - 数据ID
591
+ * @param {string} orderby - 排序字段
592
+ * @param {string} fields - 查询字段
593
+ * @returns {Promise<object|null>} - 查询到的数据对象或null
594
+ */
595
+ Store.prototype._getByIdCache = async function (id, orderby, fields) {
596
+ let way = this.config.way;
597
+ let query = this._buildQueryById(id);
598
+ let cache_key = this._genCacheKey('getObj', query);
599
+
600
+ // 尝试从缓存读取
601
+ let cached = await this._tryGetFromCache(cache_key, way);
602
+ if (cached) {
603
+ return cached;
604
+ }
605
+
606
+ // 从数据库读取
607
+ let db = this._sql.db();
608
+ db.table = this.config.table;
609
+
610
+ // 设置查询字段/排序
611
+ this._configDbQuery(db, fields, orderby);
612
+
613
+ let data = await this._getFromDb(query);
614
+
615
+ // 更新缓存(方式2)
616
+ await this._updateCache2Get(cache_key, way, data);
617
+
618
+ return data;
619
+ };
620
+
621
+ /**
622
+ * 更新数据
623
+ * @param {object} query - 查询条件
624
+ * @param {object} body - 更新内容
625
+ * @returns {Promise<number>} - 更新成功的记录数量
626
+ */
627
+ Store.prototype.set = async function (query, body) {
628
+ // 入参校验
629
+ this._validateSet(query, body);
630
+
631
+ let way = this.config.way;
632
+ let cache_key = this._genCacheKey('get', query);
633
+ let cache_obj_key = this._genCacheKey('getObj', query);
634
+
635
+ let result = await this._runSetByWay(way, query, body, cache_key, cache_obj_key);
636
+
637
+ return result.success ? 1 : 0;
638
+ };
639
+
640
+ /**
641
+ * 验证set方法参数
642
+ * @private
643
+ * @param {object} query - 查询条件
644
+ * @param {object} body - 更新内容
645
+ * @throws {TypeError} 参数验证失败时抛出
646
+ */
647
+ Store.prototype._checkSet = function (query, body) {
648
+ if (typeof query !== 'object' || query === null) {
649
+ throw new TypeError('查询条件必须是对象');
650
+ }
651
+ if (typeof body !== 'object' || body === null) {
652
+ throw new TypeError('更新内容必须是对象');
653
+ }
654
+ if (Object.keys(body).length === 0) {
655
+ throw new TypeError('更新内容不能为空');
656
+ }
657
+ };
658
+
659
+ /**
660
+ * 根据方式处理set操作
661
+ * @private
662
+ * @param {number} way - 配置方式
663
+ * @param {object} query - 查询条件
664
+ * @param {object} body - 更新内容
665
+ * @param {string} cache_key - 缓存键
666
+ * @param {string} cache_obj_key - 对象缓存键
667
+ * @returns {Promise<object>} - 更新结果
668
+ */
669
+ Store.prototype._runSetByWay = async function (way, query, body, cache_key, cache_obj_key) {
670
+ let result = { success: false };
671
+
672
+ // 方式1、2、4、2、4:写数据库
673
+ if ([1, 2, 4].includes(way)) {
674
+ result = await this._setDb(query, body);
675
+ }
676
+
677
+ // 方式3:写缓存
678
+ if (way === 3 && this._cache) {
679
+ result = await this._setCache(query, body, cache_key, cache_obj_key);
680
+ }
681
+
682
+ // 方式2:写后更新缓存
683
+ if (way === 2 && this._cache && result.success) {
684
+ await this._clearCacheAfterSet(cache_key, cache_obj_key);
685
+ }
686
+
687
+ return result;
688
+ };
689
+
690
+ /**
691
+ * 更新数据库中的数据
692
+ * @private
693
+ * @param {object} query - 查询条件
694
+ * @param {object} body - 更新内容
695
+ * @returns {Promise<object>} - 更新结果
696
+ */
697
+ Store.prototype._setDb = async function (query, body) {
698
+ let db = this._sql.db();
699
+ db.table = this.config.table;
700
+ return await this._drive.set(db, query, body);
701
+ };
702
+
703
+ /**
704
+ * 更新缓存中的数据
705
+ * @private
706
+ * @param {object} query - 查询条件
707
+ * @param {object} body - 更新内容
708
+ * @param {string} cache_key - 缓存键
709
+ * @param {string} cache_obj_key - 对象缓存键
710
+ * @returns {Promise<object>} - 更新结果
711
+ */
712
+ Store.prototype._setCache = async function (query, body, cache_key, cache_obj_key) {
713
+ // 更新列表缓存
714
+ let cached = await this.getCache(cache_key);
715
+ if (cached && Array.isArray(cached)) {
716
+ let updated = cached.map(item => {
717
+ let match = Object.keys(query).every(key => {
718
+ return item[key] === query[key];
719
+ });
720
+ if (match) {
721
+ return { ...item, ...body };
722
+ }
723
+ return item;
724
+ });
725
+ await this.setCache(cache_key, updated);
726
+ }
727
+
728
+ // 更新单条数据缓存
729
+ let item = await this.getCache(cache_obj_key);
730
+ if (item) {
731
+ await this.setCache(cache_obj_key, { ...item, ...body });
732
+ }
733
+
734
+ return { success: 1 };
735
+ };
736
+
737
+ /**
738
+ * 设置后清除缓存
739
+ * @private
740
+ * @param {string} cache_key - 缓存键
741
+ * @param {string} cache_obj_key - 对象缓存键
742
+ * @returns {Promise<void>} - 无返回值
743
+ */
744
+ Store.prototype._clearCacheAfterSet = async function (cache_key, cache_obj_key) {
745
+ await this.delCache(cache_key);
746
+ await this.delCache(cache_obj_key);
747
+ };
748
+
749
+ /**
750
+ * 设置缓存失效时间
751
+ * @param {string} key - 缓存键
752
+ * @param {number} expire_time - 失效时间(秒)
753
+ * @returns {Promise<boolean>} - 设置是否成功
754
+ */
755
+ Store.prototype.setCacheExpire = async function (key, expire_time) {
756
+ if (!this._cache) {
757
+ return false;
758
+ }
759
+
760
+ try {
761
+ await this._cache.expire(key, expire_time);
762
+ return true;
763
+ } catch (error) {
764
+ this.log('error', '设置缓存失效时间失败:', error);
765
+ return false;
766
+ }
767
+ };
768
+
769
+ /**
770
+ * 更新缓存中的数据
771
+ * @param {string} action - 操作类型
772
+ * @param {object} params - 参数
773
+ * @param {any} data - 数据
774
+ * @returns {Promise<boolean>} - 更新是否成功
775
+ */
776
+ Store.prototype.setCache = async function (action, params, data) {
777
+ if (!this._cache) {
778
+ return false;
779
+ }
780
+
781
+ try {
782
+ return await this._updateCache(action, params, data);
783
+ } catch (error) {
784
+ this.log('error', '更新缓存失败:', error);
785
+ return false;
786
+ }
787
+ };
788
+
789
+ /**
790
+ * 实际更新缓存数据
791
+ * @private
792
+ * @param {string} action - 操作类型
793
+ * @param {object} params - 参数
794
+ * @param {any} data - 数据
795
+ * @returns {Promise<boolean>} - 更新是否成功
796
+ */
797
+ Store.prototype._setCache = async function (action, params, data) {
798
+ let cache_key = this._genCacheKey(action, params);
799
+ await this.setCache(cache_key, data);
800
+ return true;
801
+ };
802
+
803
+ /**
804
+ * 根据ID更新数据
805
+ * @param {string|number} id - 数据ID
806
+ * @param {object} body - 更新内容
807
+ * @returns {Promise<number>} - 更新成功的记录数量
808
+ */
809
+ Store.prototype.setById = async function (id, body) {
810
+ // 入参校验
811
+ if (!id && id !== 0) {
812
+ throw new TypeError('ID不能为空');
813
+ }
814
+ if (typeof body !== 'object' || body === null) {
815
+ throw new TypeError('更新内容必须是对象');
816
+ }
817
+ if (Object.keys(body).length === 0) {
818
+ throw new TypeError('更新内容不能为空');
819
+ }
820
+
821
+ let way = this.config.way;
822
+ let query = this._buildQueryById(id);
823
+ let cache_keys = this._genCacheKeysForId(query);
824
+
825
+ let result = await this._handleSetById(way, query, body, cache_keys);
826
+
827
+ await this._handleCacheAfterSet(way, cache_keys);
828
+
829
+ return result;
830
+ };
831
+
832
+ /**
833
+ * 构建ID查询条件
834
+ * @private
835
+ * @param {string|number} id - 数据ID
836
+ * @returns {object} - 查询条件
837
+ */
838
+ Store.prototype._buildGetById = function (id) {
839
+ return { [this.config.id]: id };
840
+ };
841
+
842
+ /**
843
+ * 为ID生成缓存键
844
+ * @private
845
+ * @param {object} query - 查询条件
846
+ * @returns {object} - 缓存键对象
847
+ */
848
+ Store.prototype._genCacheKeysForId = function (query) {
849
+ return {
850
+ list_key: this._genCacheKey('get', query),
851
+ obj_key: this._genCacheKey('getObj', query)
852
+ };
853
+ };
854
+
855
+ /**
856
+ * 处理根据ID更新数据
857
+ * @private
858
+ * @param {number} way - 缓存方式
859
+ * @param {object} query - 查询条件
860
+ * @param {object} body - 更新内容
861
+ * @param {object} cache_keys - 缓存键
862
+ * @returns {Promise<number>} - 更新结果
863
+ */
864
+ Store.prototype._runSetById = async function (way, query, body, cache_keys) {
865
+ // 方式1、2、4、2、4:写数据库
866
+ if ([1, 2, 4].includes(way)) {
867
+ return await this._setByIdDb(query, body);
868
+ }
869
+
870
+ // 方式3:写缓存
871
+ if (way === 3 && this._cache) {
872
+ return await this._setByIdCache(query, body, cache_keys);
873
+ }
874
+
875
+ return 0;
876
+ };
877
+
878
+ /**
879
+ * 处理更新后的缓存操作
880
+ * @private
881
+ * @param {number} way - 缓存方式
882
+ * @param {object} cache_keys - 缓存键
883
+ * @returns {Promise<void>}
884
+ */
885
+ Store.prototype._runCacheAfterSet = async function (way, cache_keys) {
886
+ // 方式2:写后更新缓存
887
+ if (way === 2 && this._cache) {
888
+ await this._clearCacheSetById(cache_keys.list_key, cache_keys.obj_key);
889
+ }
890
+ };
891
+
892
+ /**
893
+ * 根据ID更新数据库中的数据
894
+ * @private
895
+ * @param {object} query - 查询条件
896
+ * @param {object} body - 更新内容
897
+ * @returns {Promise<number>} - 更新结果
898
+ */
899
+ Store.prototype._setByIdDb = async function (query, body) {
900
+ let db = this._sql.db();
901
+ db.table = this.config.table;
902
+ let result = await this._drive.set(db, query, body);
903
+ return result.success ? 1 : 0;
904
+ };
905
+
906
+ /**
907
+ * 根据ID更新缓存中的数据
908
+ * @private
909
+ * @param {object} query - 查询条件
910
+ * @param {object} body - 更新内容
911
+ * @param {object} cache_keys - 缓存键对象
912
+ * @returns {Promise<number>} - 更新结果
913
+ */
914
+ Store.prototype._setByIdCache = async function (query, body, cache_keys) {
915
+ let id = query[this.config.id];
916
+
917
+ // 更新单条数据缓存
918
+ let cached = await this.getCache(cache_keys.obj_key);
919
+ if (cached) {
920
+ await this.setCache(cache_keys.obj_key, { ...cached, ...body });
921
+ }
922
+
923
+ // 更新列表缓存
924
+ let lt = await this.getCache(cache_keys.list_key);
925
+ if (lt && Array.isArray(lt)) {
926
+ let updated = lt.map(item => {
927
+ if (item[this.config.id] === id) {
928
+ return { ...item, ...body };
929
+ }
930
+ return item;
931
+ });
932
+ await this.setCache(cache_keys.list_key, updated);
933
+ }
934
+
935
+ return 1;
936
+ };
937
+
938
+ /**
939
+ * 根据ID设置后清除缓存
940
+ * @private
941
+ * @param {string} cache_key - 缓存键
942
+ * @param {string} cache_obj_key - 对象缓存键
943
+ * @returns {Promise<void>} - 无返回值
944
+ */
945
+ Store.prototype._clearCacheSetById = async function (cache_key, cache_obj_key) {
946
+ await this.delCache(cache_key);
947
+ await this.delCache(cache_obj_key);
948
+ };
949
+
950
+ /**
951
+ * 删除数据
952
+ * @param {object} query - 查询条件
953
+ * @returns {Promise<number>} - 删除成功的记录数量
954
+ */
955
+ Store.prototype.del = async function (query) {
956
+ // 入参校验
957
+ if (typeof query !== 'object' || query === null) {
958
+ throw new TypeError('查询条件必须是对象');
959
+ }
960
+ if (Object.keys(query).length === 0) {
961
+ throw new TypeError('查询条件不能为空');
962
+ }
963
+
964
+ let way = this.config.way;
965
+ let cache_key = this._genCacheKey('get', query);
966
+ let cache_obj_key = this._genCacheKey('getObj', query);
967
+
968
+ let affected_rows = 0;
969
+
970
+ // 方式1、2、4、2、4:删除数据库中的数据
971
+ if ([1, 2, 4].includes(way)) {
972
+ affected_rows = await this._delDb(query);
973
+ }
974
+
975
+ // 方式3:删除缓存中的数据据
976
+ if (way === 3 && this._cache) {
977
+ affected_rows = await this._delCache(query, cache_key, cache_obj_key);
978
+ }
979
+
980
+ // 方式2:删除数据后清除相关缓存
981
+ if (way === 2 && this._cache) {
982
+ await this._clearCacheAfterDel(cache_key, cache_obj_key);
983
+ }
984
+
985
+ return affected_rows;
986
+ };
987
+
988
+ /**
989
+ * 删除数据库中的数据
990
+ * @private
991
+ * @param {object} query - 查询条件
992
+ * @returns {Promise<number>} - 影响行数
993
+ */
994
+ Store.prototype._delDb = async function (query) {
995
+ let db = this._sql.db();
996
+ db.table = this.config.table;
997
+
998
+ let result = await this._drive.del(db, query);
999
+ return result.success ? 1 : 0;
1000
+ };
1001
+
1002
+ /**
1003
+ * 删除缓存中的数据据
1004
+ * @private
1005
+ * @param {object} query - 查询条件
1006
+ * @param {string} cache_key - 缓存键
1007
+ * @param {string} cache_obj_key - 对象缓存键
1008
+ * @returns {Promise<number>} - 影响行数
1009
+ */
1010
+ Store.prototype._delCache = async function (query, cache_key, cache_obj_key) {
1011
+ // 从缓存获取当前数据
1012
+ let cached = await this.getCache(cache_key);
1013
+ let affected_rows = 0;
1014
+
1015
+ if (cached && Array.isArray(cached)) {
1016
+ // 过滤掉需要删除的数据
1017
+ let filtered = cached.filter(item => {
1018
+ // 检查item是否匹配query条件
1019
+ return !Object.keys(query).every(key => {
1020
+ return item[key] === query[key];
1021
+ });
1022
+ });
1023
+ await this.setCache(cache_key, filtered);
1024
+ affected_rows = cached.length - filtered.length;
1025
+ }
1026
+
1027
+ // 同时删除单条数据缓存
1028
+ await this.delCache(cache_obj_key);
1029
+ return affected_rows;
1030
+ };
1031
+
1032
+ /**
1033
+ * 删除数据后清除相关缓存
1034
+ * @private
1035
+ * @param {string} cache_key - 缓存键
1036
+ * @param {string} cache_obj_key - 对象缓存键
1037
+ * @returns {Promise<void>} - 无返回值
1038
+ */
1039
+ Store.prototype._clearCacheAfterDel = async function (cache_key, cache_obj_key) {
1040
+ // 清除相关缓存,下次读取时会自动更新
1041
+ await this.delCache(cache_key);
1042
+ await this.delCache(cache_obj_key);
1043
+
1044
+ // 清除空查询的列表缓存
1045
+ let all_cache_key = this._genCacheKey('get', {});
1046
+ await this.delCache(all_cache_key);
1047
+ };
1048
+
1049
+ /**
1050
+ * 根据ID删除数据
1051
+ * @param {string|number} id - 数据ID
1052
+ * @returns {Promise<number>} - 删除成功的记录数量
1053
+ */
1054
+ Store.prototype.delById = async function (id) {
1055
+ // 入参校验
1056
+ if (!id && id !== 0) {
1057
+ throw new TypeError('ID不能为空');
1058
+ }
1059
+
1060
+ try {
1061
+ let way = this.config.way;
1062
+ // 构建查询条件
1063
+ const query = { [this.config.id]: id };
1064
+
1065
+ let cache_key = this._genCacheKey('get', query);
1066
+ let cache_obj_key = this._genCacheKey('getObj', query);
1067
+
1068
+ let affected_rows = 0;
1069
+
1070
+ // 方式1、2、4、2、4:删除数据库中的数据
1071
+ if ([1, 2, 4].includes(way)) {
1072
+ affected_rows = await this._delByIdDb(query);
1073
+ }
1074
+
1075
+ // 方式3:删除缓存中的数据据
1076
+ if (way === 3 && this._cache) {
1077
+ affected_rows = await this._delByIdCache(id, cache_key, cache_obj_key);
1078
+ }
1079
+
1080
+ // 方式2:删除数据后清除相关缓存
1081
+ if (way === 2 && this._cache) {
1082
+ await this._clearCacheAfterDel(cache_key, cache_obj_key);
1083
+ }
1084
+
1085
+ return affected_rows;
1086
+ } catch (error) {
1087
+ this.log('error', '根据ID删除数据失败:', error);
1088
+ return 0;
1089
+ }
1090
+ };
1091
+
1092
+ /**
1093
+ * 根据ID删除数据库中的数据
1094
+ * @private
1095
+ * @param {object} query - 查询条件
1096
+ * @returns {Promise<number>} - 影响行数
1097
+ */
1098
+ Store.prototype._delByIdDb = async function (query) {
1099
+ let db = this._sql.db();
1100
+ db.table = this.config.table;
1101
+
1102
+ let result = await this._drive.del(db, query);
1103
+ return result.success ? 1 : 0;
1104
+ };
1105
+
1106
+ /**
1107
+ * 根据ID删除缓存中的数据据
1108
+ * @private
1109
+ * @param {string|number} id - 数据ID
1110
+ * @param {string} cache_key - 缓存键
1111
+ * @param {string} cache_obj_key - 对象缓存键
1112
+ * @returns {Promise<number>} - 影响行数
1113
+ */
1114
+ Store.prototype._delByIdCache = async function (id, cache_key, cache_obj_key) {
1115
+ // 从缓存获取当前数据
1116
+ let all_cache_key = this._genCacheKey('get', {});
1117
+ let cached = await this.getCache(all_cache_key);
1118
+ let affected_rows = 0;
1119
+
1120
+ if (cached && Array.isArray(cached)) {
1121
+ // 过滤掉需要删除的数据
1122
+ let filtered = cached.filter(item => {
1123
+ return item[this.config.id] !== id;
1124
+ });
1125
+ await this.setCache(all_cache_key, filtered);
1126
+ affected_rows = cached.length - filtered.length;
1127
+ }
1128
+
1129
+ // 同时删除单条数据缓存
1130
+ await this.delCache(cache_key);
1131
+ await this.delCache(cache_obj_key);
1132
+
1133
+ return affected_rows;
1134
+ };
1135
+
1136
+ /**
1137
+ * 根据ID删除数据后清除相关缓存
1138
+ * @private
1139
+ * @param {string} cache_key - 缓存键
1140
+ * @param {string} cache_obj_key - 对象缓存键
1141
+ * @returns {Promise<void>} - 无返回值
1142
+ */
1143
+ Store.prototype._clearCacheAfterDel = async function (cache_key, cache_obj_key) {
1144
+ // 清除相关缓存,下次读取时会自动更新
1145
+ await this.delCache(cache_key);
1146
+ await this.delCache(cache_obj_key);
1147
+
1148
+ // 清除空查询的列表缓存
1149
+ let all_cache_key = this._genCacheKey('get', {});
1150
+ await this.delCache(all_cache_key);
1151
+ };
1152
+
1153
+ /**
1154
+ * 统计记录数量
1155
+ * @param {object} query - 查询条件
1156
+ * @returns {Promise<number>} - 记录数量
1157
+ */
1158
+ Store.prototype.count = async function (query = {}) {
1159
+ // 入参校验
1160
+ if (typeof query !== 'object' || query === null) {
1161
+ throw new TypeError('查询条件必须是对象');
1162
+ }
1163
+
1164
+ try {
1165
+ let db = this._sql.db();
1166
+ db.table = this.config.table;
1167
+ db.count_ret = 'true';
1168
+
1169
+ let result = await this._drive.getMain(db, query, 'count');
1170
+ return result.result ? result.result.count : 0;
1171
+ } catch (error) {
1172
+ this.log('error', '统计数据失败:', error);
1173
+ return 0;
1174
+ }
1175
+ };
1176
+
1177
+ /**
1178
+ * 统计字段总和
1179
+ * @param {object} query - 查询条件
1180
+ * @param {string} field - 统计字段
1181
+ * @returns {Promise<number>} - 总和
1182
+ */
1183
+ Store.prototype.sum = async function (query, field) {
1184
+ // 入参校验
1185
+ if (!field || typeof field !== 'string') {
1186
+ throw new TypeError('字段名必须是字符串');
1187
+ }
1188
+
1189
+ try {
1190
+ let db = this._sql.db();
1191
+ db.table = this.config.table;
1192
+ return await db.sum(query, field);
1193
+ } catch (error) {
1194
+ this.log('error', '统计字段总和失败:', error);
1195
+ return 0;
1196
+ }
1197
+ };
1198
+
1199
+ /**
1200
+ * 统计字段平均值
1201
+ * @param {object} query - 查询条件
1202
+ * @param {string} field - 统计字段
1203
+ * @returns {Promise<number>} - 平均值
1204
+ */
1205
+ Store.prototype.avg = async function (query, field) {
1206
+ // 入参校验
1207
+ if (!field || typeof field !== 'string') {
1208
+ throw new TypeError('字段名必须是字符串');
1209
+ }
1210
+
1211
+ try {
1212
+ let db = this._sql.db();
1213
+ db.table = this.config.table;
1214
+ return await db.avg(query, field);
1215
+ } catch (error) {
1216
+ this.log('error', '统计字段平均值失败:', error);
1217
+ return 0;
1218
+ }
1219
+ };
1220
+
1221
+ /**
1222
+ * 统计字段最大值
1223
+ * @param {object} query - 查询条件
1224
+ * @param {string} field - 统计字段
1225
+ * @returns {Promise<number>} - 最大值
1226
+ */
1227
+ Store.prototype.max = async function (query, field) {
1228
+ // 入参校验
1229
+ if (!field || typeof field !== 'string') {
1230
+ throw new TypeError('字段名必须是字符串');
1231
+ }
1232
+
1233
+ try {
1234
+ let db = this._sql.db();
1235
+ db.table = this.config.table;
1236
+ return await db.max(query, field);
1237
+ } catch (error) {
1238
+ this.log('error', '统计字段最大值失败:', error);
1239
+ return 0;
1240
+ }
1241
+ };
1242
+
1243
+ /**
1244
+ * 统计字段最小值
1245
+ * @param {object} query - 查询条件
1246
+ * @param {string} field - 统计字段
1247
+ * @returns {Promise<number>} - 最小值
1248
+ */
1249
+ Store.prototype.min = async function (query, field) {
1250
+ // 入参校验
1251
+ if (!field || typeof field !== 'string') {
1252
+ throw new TypeError('字段名必须是字符串');
1253
+ }
1254
+
1255
+ try {
1256
+ let db = this._sql.db();
1257
+ db.table = this.config.table;
1258
+ return await db.min(query, field);
1259
+ } catch (error) {
1260
+ this.log('error', '统计字段最小值失败:', error);
1261
+ return 0;
1262
+ }
1263
+ };
1264
+
1265
+ /**
1266
+ * 去重查询
1267
+ * @param {object} query - 查询条件
1268
+ * @param {string} field - 去重字段
1269
+ * @param {string} orderby - 排序字段
1270
+ * @returns {Promise<Array>} - 查询结果数组
1271
+ */
1272
+ Store.prototype.distinct = async function (query = {}, field, orderby = '') {
1273
+ // 入参校验
1274
+ if (!field || typeof field !== 'string') {
1275
+ throw new TypeError('去重字段必须是字符串');
1276
+ }
1277
+
1278
+ try {
1279
+ let db = this._sql.db();
1280
+ db.table = this.config.table;
1281
+ return await db.distinct(query, field, orderby);
1282
+ } catch (error) {
1283
+ this.log('error', '去重查询数据失败:', error);
1284
+ return [];
1285
+ }
1286
+ };
1287
+
1288
+ /**
1289
+ * 分组查询
1290
+ * @param {object} query - 查询条件
1291
+ * @param {string} groupby - 分组字段
1292
+ * @param {string} fields - 查询字段
1293
+ * @param {string} orderby - 排序字段
1294
+ * @returns {Promise<Array>} - 查询结果数组
1295
+ */
1296
+ Store.prototype.group = async function (query, groupby, fields = '*', orderby = '') {
1297
+ // 入参校验
1298
+ if (!groupby || typeof groupby !== 'string') {
1299
+ throw new TypeError('分组字段必须是字符串');
1300
+ }
1301
+
1302
+ try {
1303
+ let db = this._sql.db();
1304
+ db.table = this.config.table;
1305
+ return await db.group(query, groupby, fields, orderby);
1306
+ } catch (error) {
1307
+ this.log('error', '分组查询数据失败:', error);
1308
+ return [];
1309
+ }
1310
+ };
1311
+
1312
+ /**
1313
+ * 开始事务
1314
+ * @returns {Promise<object>} - 事务对象
1315
+ */
1316
+ Store.prototype.begin = async function () {
1317
+ try {
1318
+ let db = this._sql.db();
1319
+ return await db.begin();
1320
+ } catch (error) {
1321
+ this.log('error', '开始事务失败:', error);
1322
+ return null;
1323
+ }
1324
+ };
1325
+
1326
+ /**
1327
+ * 提交事务
1328
+ * @param {object} txn - 事务对象
1329
+ * @returns {Promise<boolean>} - 提交结果
1330
+ */
1331
+ Store.prototype.commit = async function (txn) {
1332
+ // 入参校验
1333
+ if (!txn) {
1334
+ throw new TypeError('事务对象不能为空');
1335
+ }
1336
+
1337
+ try {
1338
+ let db = this._sql.db();
1339
+ return await db.commit(txn);
1340
+ } catch (error) {
1341
+ this.log('error', '提交事务失败:', error);
1342
+ return false;
1343
+ }
1344
+ };
1345
+
1346
+ /**
1347
+ * 回滚事务
1348
+ * @param {object} txn - 事务对象
1349
+ * @returns {Promise<boolean>} - 回滚结果
1350
+ */
1351
+ Store.prototype.rollback = async function (txn) {
1352
+ // 入参校验
1353
+ if (!txn) {
1354
+ throw new TypeError('事务对象不能为空');
1355
+ }
1356
+
1357
+ try {
1358
+ let db = this._sql.db();
1359
+ return await db.rollback(txn);
1360
+ } catch (error) {
1361
+ this.log('error', '回滚事务失败:', error);
1362
+ return false;
1363
+ }
1364
+ };
1365
+
1366
+ /**
1367
+ * 获取缓存
1368
+ * @param {string} key - 缓存键名
1369
+ * @returns {Promise<*>|null} - 缓存值或null
1370
+ */
1371
+ Store.prototype.getCache = async function (key) {
1372
+ // 入参校验
1373
+ if (!key) {
1374
+ throw new TypeError('缓存键名不能为空');
1375
+ }
1376
+
1377
+ try {
1378
+ if (this._cache) {
1379
+ return await this._cache.get(key);
1380
+ }
1381
+ return null;
1382
+ } catch (error) {
1383
+ this.log('error', '获取缓存失败:', error);
1384
+ return null;
1385
+ }
1386
+ };
1387
+
1388
+ /**
1389
+ * 设置缓存
1390
+ * @param {string} key - 缓存键名
1391
+ * @param {*} value - 缓存值
1392
+ * @param {number} expire - 过期时间(秒)
1393
+ * @returns {Promise<boolean>} - 设置结果
1394
+ */
1395
+ Store.prototype.setCache = async function (key, value, expire = 3600) {
1396
+ // 入参校验
1397
+ if (!key) {
1398
+ throw new TypeError('缓存键名不能为空');
1399
+ }
1400
+
1401
+ try {
1402
+ if (this._cache) {
1403
+ return await this._cache.set(key, value, expire);
1404
+ }
1405
+ return false;
1406
+ } catch (error) {
1407
+ this.log('error', '设置缓存失败:', error);
1408
+ return false;
1409
+ }
1410
+ };
1411
+
1412
+ /**
1413
+ * 删除缓存
1414
+ * @param {string} key - 缓存键名
1415
+ * @returns {Promise<boolean>} - 删除结果
1416
+ */
1417
+ Store.prototype.delCache = async function (key) {
1418
+ // 入参校验
1419
+ if (!key) {
1420
+ throw new TypeError('缓存键名不能为空');
1421
+ }
1422
+
1423
+ try {
1424
+ if (this._cache) {
1425
+ return await this._cache.del(key);
1426
+ }
1427
+ return false;
1428
+ } catch (error) {
1429
+ this.log('error', '删除缓存失败:', error);
1430
+ return false;
1431
+ }
1432
+ };
1433
+
1434
+ /**
1435
+ * 生成缓存键
1436
+ * @param {string} action - 操作类型(get/set/add/del)
1437
+ * @param {object} params - 参数对象
1438
+ * @returns {string} - 生成的缓存键
1439
+ */
1440
+ Store.prototype._genCacheKey = function (action, params = {}) {
1441
+ let base = `${this.config.table}:${action}`;
1442
+
1443
+ if (!params || typeof params !== 'object') {
1444
+ return base;
1445
+ }
1446
+
1447
+ // 对参数进行排序,确保相同参数生成相同的键
1448
+ let sorted = Object.keys(params)
1449
+ .sort()
1450
+ .map(key => `${key}:${JSON.stringify(params[key])}`)
1451
+ .join('_');
1452
+
1453
+ return `${base}:${sorted}`;
1454
+ };
1455
+
1456
+ /**
1457
+ * 启动定时任务
1458
+ */
1459
+ Store.prototype._startTimers = function () {
1460
+ // 清除已有定时器
1461
+ this._clearTimers();
1462
+
1463
+ let way = this.config.way;
1464
+
1465
+ // 方式3:读缓存,写缓存,定时持久存
1466
+ if (way === 3 && this._cache) {
1467
+ this._startSaveTimer();
1468
+ }
1469
+
1470
+ // 方式4:读缓存,写持久存,定时更新缓存
1471
+ if (way === 4 && this._cache) {
1472
+ this._startCacheTimer();
1473
+ }
1474
+ };
1475
+
1476
+ /**
1477
+ * 清除定时任务
1478
+ */
1479
+ Store.prototype._clearTimers = function () {
1480
+ if (this._save_timer) {
1481
+ clearInterval(this._save_timer);
1482
+ this._save_timer = null;
1483
+ }
1484
+
1485
+ if (this._cache_timer) {
1486
+ clearInterval(this._cache_timer);
1487
+ this._cache_timer = null;
1488
+ }
1489
+ };
1490
+
1491
+ /**
1492
+ * 启动定时持久化定时器
1493
+ */
1494
+ Store.prototype._startSaveTimer = function () {
1495
+ // 定时将缓存数据持久化到数据库
1496
+ this._save_timer = setInterval(() => {
1497
+ this._saveCacheToDb().catch(error => {
1498
+ this.log('error', '定时持久化数据失败:', error);
1499
+ });
1500
+ }, this._save_interval * 1000);
1501
+
1502
+ this.log('info', `已启动定时持久化定时器,间隔${this._save_interval}秒`);
1503
+ };
1504
+
1505
+ /**
1506
+ * 启动定时更新缓存定时器
1507
+ */
1508
+ Store.prototype._startCacheTimer = function () {
1509
+ // 定时从数据库更新缓存
1510
+ this._cache_timer = setInterval(() => {
1511
+ this._updateCacheFromDb().catch(error => {
1512
+ this.log('error', '定时更新缓存失败:', error);
1513
+ });
1514
+ }, this._cache_interval * 1000);
1515
+
1516
+ this.log('info', `已启动定时更新缓存定时器,间隔${this._cache_interval}秒`);
1517
+ };
1518
+
1519
+ /**
1520
+ * 将缓存数据持久化到数据库
1521
+ * @returns {Promise<void>}
1522
+ */
1523
+ Store.prototype._saveCacheToDb = async function () {
1524
+ try {
1525
+ // 获取缓存中的所有数据
1526
+ let cache_key = this._genCacheKey('get', {});
1527
+ let cached = await this.getCache(cache_key);
1528
+
1529
+ if (!cached || !Array.isArray(cached)) {
1530
+ return;
1531
+ }
1532
+
1533
+ let db = this._sql.db();
1534
+ db.table = this.config.table;
1535
+
1536
+ // 批量保存数据到数据库
1537
+ await this._drive.addList(db, cached);
1538
+
1539
+ this.log('info', `成功持久化${cached.length}条数据到数据库`);
1540
+ } catch (error) {
1541
+ this.log('error', '持久化缓存到数据库失败:', error);
1542
+ throw error;
1543
+ }
1544
+ };
1545
+
1546
+ /**
1547
+ * 从数据库更新缓存
1548
+ * @returns {Promise<void>}
1549
+ */
1550
+ Store.prototype._setCacheFromDb = async function () {
1551
+ try {
1552
+ // 从数据库获取最新数据
1553
+ let db = this._sql.db();
1554
+ db.table = this.config.table;
1555
+
1556
+ let result = await this._drive.get(db, {});
1557
+ let data = result.result ? result.result.list : [];
1558
+
1559
+ // 更新缓存
1560
+ let cache_key = this._genCacheKey('get', {});
1561
+ await this.setCache(cache_key, data);
1562
+
1563
+ // 更新单条数据缓存
1564
+ for (let item of data) {
1565
+ let cache_obj_key = this._genCacheKey('getObj', { [this.config.id]: item[this.config.id] });
1566
+ await this.setCache(cache_obj_key, item);
1567
+ }
1568
+
1569
+ this.log('info', `成功从数据库更新${data.length}条数据到缓存`);
1570
+ } catch (error) {
1571
+ this.log('error', '从数据库更新缓存失败:', error);
1572
+ throw error;
1573
+ }
1574
+ };
1575
+
1576
+ /**
1577
+ * 关闭定时器和资源
1578
+ */
1579
+ Store.prototype.close = function () {
1580
+ this._clearTimers();
1581
+ };
1582
+
1583
+ /**
1584
+ * 构建ID查询条件
1585
+ * @private
1586
+ * @param {string|number} id - 数据ID
1587
+ * @returns {object} - 查询条件
1588
+ */
1589
+ Store.prototype._buildQueryById = function (id) {
1590
+ return { [this.config.id]: id };
1591
+ };
1592
+
1593
+ /**
1594
+ * 处理根据ID更新数据
1595
+ * @private
1596
+ * @param {number} way - 缓存方式
1597
+ * @param {object} query - 查询条件
1598
+ * @param {object} body - 更新内容
1599
+ * @param {object} cache_keys - 缓存键
1600
+ * @returns {Promise<number>} - 更新结果
1601
+ */
1602
+ Store.prototype._handleSetById = async function (way, query, body, cache_keys) {
1603
+ return await this._runSetById(way, query, body, cache_keys);
1604
+ };
1605
+
1606
+ /**
1607
+ * 处理更新后的缓存操作
1608
+ * @private
1609
+ * @param {number} way - 缓存方式
1610
+ * @param {object} cache_keys - 缓存键
1611
+ * @returns {Promise<void>}
1612
+ */
1613
+ Store.prototype._handleCacheAfterSet = async function (way, cache_keys) {
1614
+ return await this._runCacheAfterSet(way, cache_keys);
1615
+ };
1616
+
1617
+ /**
1618
+ * 根据ID从数据库获取数据
1619
+ * @private
1620
+ * @param {string|number} id - 数据ID
1621
+ * @param {string} orderby - 排序字段
1622
+ * @param {string} fields - 查询字段
1623
+ * @returns {Promise<object|null>} - 查询到的数据对象或null
1624
+ */
1625
+ Store.prototype._getByIdDb = async function (id, orderby, fields) {
1626
+ let query = this._buildQueryById(id);
1627
+ let db = this._sql.db();
1628
+ db.table = this.config.table;
1629
+
1630
+ // 设置查询字段/排序
1631
+ this._configDbQuery(db, fields, orderby);
1632
+
1633
+ return await this._getFromDb(query);
1634
+ };
1635
+
1636
+ /**
1637
+ * 从数据库获取列表数据
1638
+ * @private
1639
+ * @param {object} query - 查询条件
1640
+ * @returns {Promise<Array>} - 数据列表
1641
+ */
1642
+ Store.prototype._getListFromDb = async function (query) {
1643
+ let db = this._sql.db();
1644
+ db.table = this.config.table;
1645
+
1646
+ let result = await this._drive.get(db, query);
1647
+ return result.result ? result.result.list : [];
1648
+ };
1649
+
1650
+ /**
1651
+ * 更新方式2的缓存
1652
+ * @private
1653
+ * @param {object} data - 要缓存的数据
1654
+ * @returns {Promise<void>}
1655
+ */
1656
+ Store.prototype._updateCacheForWay2 = async function (data) {
1657
+ if (!this._cache) {
1658
+ return;
1659
+ }
1660
+
1661
+ let cache_key = this._genCacheKey('get', {});
1662
+ let cached = await this.getCache(cache_key) || [];
1663
+
1664
+ // 查找是否已存在
1665
+ let idx = cached.findIndex(item => item[this.config.id] === data[this.config.id]);
1666
+ if (idx >= 0) {
1667
+ cached[idx] = data;
1668
+ } else {
1669
+ cached.push(data);
1670
+ }
1671
+
1672
+ await this.setCache(cache_key, cached);
1673
+ };
1674
+
1675
+ /**
1676
+ * 更新方式2的列表缓存
1677
+ * @private
1678
+ * @param {Array} list - 数据列表
1679
+ * @returns {Promise<void>}
1680
+ */
1681
+ Store.prototype._updateCacheForWay2List = async function (list) {
1682
+ if (!this._cache) {
1683
+ return;
1684
+ }
1685
+
1686
+ let cache_key = this._genCacheKey('get', {});
1687
+ await this.setCache(cache_key, list);
1688
+ };
1689
+
1690
+ /**
1691
+ * 更新缓存(用于set操作后)
1692
+ * @private
1693
+ * @param {object} query - 查询条件
1694
+ * @param {object} body - 更新内容
1695
+ * @param {string} cache_obj_key - 对象缓存键
1696
+ * @returns {Promise<void>}
1697
+ */
1698
+ Store.prototype._updateCache2Set = async function (query, body, cache_obj_key) {
1699
+ if (!this._cache) {
1700
+ return;
1701
+ }
1702
+
1703
+ // 更新单条数据缓存
1704
+ let item = await this.getCache(cache_obj_key);
1705
+ if (item) {
1706
+ await this.setCache(cache_obj_key, { ...item, ...body });
1707
+ }
1708
+ };
1709
+
1710
+ /**
1711
+ * 批量添加数据处理(按way分发)
1712
+ * @private
1713
+ * @param {Array} list - 要添加的数据列表
1714
+ * @param {number} way - 读写方式
1715
+ * @returns {Promise<object>} - 添加结果
1716
+ */
1717
+ Store.prototype._addListByWay = async function (list, way) {
1718
+ let result;
1719
+
1720
+ // 方式1、2、4:写数据库
1721
+ if ([1, 2, 4].includes(way)) {
1722
+ result = await this._addListToDb(list);
1723
+ }
1724
+
1725
+ // 方式3:写缓存
1726
+ if (way === 3 && this._cache) {
1727
+ result = await this._addListToCache(list);
1728
+ }
1729
+
1730
+ // 方式2:写后更新缓存
1731
+ if (way === 2 && this._cache && result && result.result) {
1732
+ // 清除列表缓存,下次读取时会自动更新
1733
+ let cache_key = this._genCacheKey('get', {});
1734
+ await this.delCache(cache_key);
1735
+ }
1736
+
1737
+ return result;
1738
+ };
1739
+
1740
+ /**
1741
+ * 初始化驱动
1742
+ * @private
1743
+ * @returns {Promise<void>}
1744
+ */
1745
+ Store.prototype._initDrive = async function () {
1746
+ if (this._drive) {
1747
+ return;
1748
+ }
1749
+
1750
+ this._drive = new Drive(this.config);
1751
+ this._drive.sql = this._sql;
1752
+ this._drive.cache = this._cache;
1753
+ };
1754
+
1755
+ exports.Store = Store;