Compare commits
590 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72d70e90f1 | |||
| 3bede99cc6 | |||
| 95aaf80f3a | |||
| 77ed812901 | |||
| 2e6a0abac3 | |||
| d5056af676 | |||
| 1e685340f8 | |||
| 7ec9fb3f0b | |||
| d430307b48 | |||
| 5113e5953a | |||
| 766fda7292 | |||
| 1bc49ea249 | |||
| 70fad51f40 | |||
| 6764684fe7 | |||
| da2c296c05 | |||
| bdf74652b3 | |||
| 70e8627a96 | |||
| c2c6577064 | |||
| 260c7d054c | |||
| d25933cf26 | |||
| 4aa56441ce | |||
| de68151539 | |||
| 04b46d8025 | |||
| bc4355cad5 | |||
| 968d996a9b | |||
| d6e5d89768 | |||
| 1b5f715dec | |||
| da8e1d0191 | |||
| 33cdf7278e | |||
| 3b12802900 | |||
| e6b307b0dc | |||
| b8e0301175 | |||
| e2da441a6d | |||
| dca6cc5dd4 | |||
| 5c99ff7475 | |||
| 0283c250e4 | |||
| 5bd52df240 | |||
| 5ad403bf86 | |||
| ce3844957f | |||
| 6c0f655d0a | |||
| 29b820b846 | |||
| 34311889e8 | |||
| ba60ed541c | |||
| 35eae7b3ec | |||
| ca85671a4c | |||
| d7e2777c13 | |||
| 566f2d9a15 | |||
| c01ce6d1e6 | |||
| c66671bf5f | |||
| 44ff3b19d6 | |||
| ae27eb142d | |||
| d7deaa346b | |||
| 1200f36d14 | |||
| 1b2e211bfa | |||
| 851682d579 | |||
| b40eb35016 | |||
| 3ef2910058 | |||
| 0637f5fb6c | |||
| 4405d1f3f1 | |||
| 9aea487537 | |||
| f81635f997 | |||
| 7829ac9931 | |||
| 4224be8567 | |||
| 534891309c | |||
| 9996fcfd74 | |||
| a669bfda6c | |||
| aa13239e89 | |||
| c4ab53db12 | |||
| d185f334ac | |||
| 1313140e45 | |||
| da934a9bbb | |||
| 1f3d3d8a7c | |||
| 6ca101727a | |||
| c697e668e3 | |||
| fd0939fe0a | |||
| 365d69e0c6 | |||
| a74ab4e5e7 | |||
| a1be06854f | |||
| e1399be538 | |||
| 200a358933 | |||
| 32abff626e | |||
| 98317dce1f | |||
| 31e1139cd0 | |||
| a4fbfbd145 | |||
| b2d1b10962 | |||
| b9b067707b | |||
| 3daa03eab2 | |||
| 01b6d1cc14 | |||
| aa94b47773 | |||
| 148fa1f2bb | |||
| bf1600b385 | |||
| bf747e22ce | |||
| 2aabbfd8b9 | |||
| 9dcf362048 | |||
| 549bfd074c | |||
| e38c806aee | |||
| 7124e6934b | |||
| ecd2d37c70 | |||
| 3e711551e7 | |||
| 75306941c2 | |||
| 4f3148eaa2 | |||
| 30880f8c30 | |||
| 9dc47ec7ad | |||
| 2cc4135b28 | |||
| cb075c907d | |||
| f44a94b451 | |||
| f2b0c2987f | |||
| d4e8aba1fd | |||
| 19d55eb09b | |||
| ca8e5ff867 | |||
| 37f515d4a8 | |||
| b94aa6137c | |||
| 84fe20396b | |||
| e54c1bbc97 | |||
| a35a9a1038 | |||
| 26df398392 | |||
| 2ad1ae4f40 | |||
| 362ed5ad9d | |||
| f049cbce6c | |||
| 3a59a2755c | |||
| f46c8aed0c | |||
| e6aa9696a9 | |||
| f23479c4d1 | |||
| e4c41d6500 | |||
| 637df63953 | |||
| baa62a9c77 | |||
| a90b1cc012 | |||
| d3207cc2b8 | |||
| 2796d9bad7 | |||
| 375af57afe | |||
| 42317e4ed2 | |||
| 6b6fd411c5 | |||
| 04d9c0b515 | |||
| 342c9df3ff | |||
| 3761f8a88e | |||
| 458313af9b | |||
| e73a4675f3 | |||
| 901cdfeb5c | |||
| b203cfcb7f | |||
| 9f366d55fa | |||
| 30304da0c8 | |||
| cb18e27f18 | |||
| 4eeaec45c4 | |||
| d377314b40 | |||
| 5eeeeb5006 | |||
| a1f7c09b2a | |||
| 10b123ab93 | |||
| 97aea66f7c | |||
| 07d8c719ac | |||
| 203be4307d | |||
| bd4f5b83e1 | |||
| 3e2f9e7210 | |||
| ef4ca397ce | |||
| 4cae93ef34 | |||
| aaa2f1b22f | |||
| bee5104661 | |||
| 349fc3e129 | |||
| 09898ec5c2 | |||
| d5c336e946 | |||
| b6c2fa58f5 | |||
| d7c7b56b95 | |||
| 545a53bb68 | |||
| 9127eeaf31 | |||
| f0b71b47a0 | |||
| f569876225 | |||
| 8669dcd9b0 | |||
| 66554a1376 | |||
| b62a3d0e5d | |||
| 026dad9374 | |||
| 687c2f12ee | |||
| 4b0be88fca | |||
| bb42147974 | |||
| 8d7d9fc485 | |||
| 6cd566bc30 | |||
| 408df2f09c | |||
| 011658461e | |||
| 3ab2eb0535 | |||
| a29e15faba | |||
| 8e97922012 | |||
| 548d3eae00 | |||
| 6f7e462589 | |||
| cf9e43cdd8 | |||
| 426ae41f54 | |||
| 5b21dc0bd5 | |||
| 67d4fb097d | |||
| 0008141989 | |||
| c4ca0175dd | |||
| 193d77b5b7 | |||
| 0c88c76417 | |||
| 843bd8a814 | |||
| 348220bc7b | |||
| d6c18f0774 | |||
| e1c76fd8ec | |||
| bc6a960451 | |||
| 4e87436cc0 | |||
| 942ffa29a1 | |||
| b44e1a0e7c | |||
| d22ddac9cd | |||
| ccab7c98e4 | |||
| 3334537663 | |||
| 0c35e2ce7d | |||
| db11438f5c | |||
| 9f3e800e59 | |||
| 8d8310fd2c | |||
| 12c6dc515f | |||
| c2c2383305 | |||
| 4a92324774 | |||
| a4bd19f950 | |||
| f71d04f8af | |||
| 4b10efb13c | |||
| b4c70d4d9c | |||
| f624a8bf5e | |||
| 8ce553a9e4 | |||
| 5b064b4015 | |||
| 6228534155 | |||
| d235130d11 | |||
| f0982839e0 | |||
| ff8a8d2b97 | |||
| f2078ea54a | |||
| c463875fba | |||
| 7c5232e71b | |||
| 2c9b4777ae | |||
| 93f67812ae | |||
| e5b75e3879 | |||
| 67575c17bc | |||
| 7ac9e49212 | |||
| ff45c59946 | |||
| 8d48576305 | |||
| af8689d627 | |||
| 2910c9186a | |||
| b09d32b1d7 | |||
| 403d46b777 | |||
| 85bd5254c1 | |||
| 5050f76066 | |||
| 1ee3e638f7 | |||
| 94e8768424 | |||
| 675711cdcf | |||
| e66ee67cf7 | |||
| 40eb57ee47 | |||
| 6a8e8f1f7d | |||
| 5c83c19bce | |||
| 86c9073da8 | |||
| 43c1839345 | |||
| f62cc1c4a9 | |||
| f6d2069e1a | |||
| f33e14f60f | |||
| d6f275b2d1 | |||
| d8de5a68eb | |||
| bd8729d473 | |||
| 3fd97aa43f | |||
| 9d6876684b | |||
| 47ed819b9d | |||
| b1dce77e51 | |||
| 21607559c4 | |||
| af6a00ee47 | |||
| 324a533c94 | |||
| c1f71050e9 | |||
| db32c37318 | |||
| 3d5741f5fd | |||
| c4c9723b7b | |||
| a32749cef8 | |||
| be8275b936 | |||
| 169b2c79cb | |||
| 33ad309eeb | |||
| ebaaa86f09 | |||
| 71afbf5ff9 | |||
| 4e046021e3 | |||
| 4cbb4bb859 | |||
| 0038f20334 | |||
| 197af0181c | |||
| 1830fcd43e | |||
| 53845422c1 | |||
| 757d38645e | |||
| 5ee6cbce8f | |||
| fd39eb6450 | |||
| 89fbbbb75f | |||
| e150969ee3 | |||
| 4c6843afb4 | |||
| eb0786ca27 | |||
| 7299c8ebe6 | |||
| bcdcaa5631 | |||
| fab26ffca4 | |||
| 6a93346e87 | |||
| df0dfd62c6 | |||
| 51a873049e | |||
| 05820438d0 | |||
| 3b967aa449 | |||
| fa437b30aa | |||
| bcec36f7e2 | |||
| 8c0dc6c815 | |||
| 9b6548c1b4 | |||
| b4d31d3133 | |||
| 6d8cb7ca4e | |||
| 503feb1b21 | |||
| 50a843c9ef | |||
| 8a5f6dc34e | |||
| 38a01f4a6e | |||
| ca544d7605 | |||
| ac8c8c56a6 | |||
| 8a2e889048 | |||
| b611f132f1 | |||
| 759caadb21 | |||
| 4250f27e11 | |||
| 77ab434d17 | |||
| 21661eb748 | |||
| 5e84b473f6 | |||
| e142405bb3 | |||
| 632bd20e7d | |||
| aac0324616 | |||
| 18b45b223c | |||
| 035da5293b | |||
| 1290676fe4 | |||
| 73de8ad04f | |||
| 9a7b765b71 | |||
| 4fb8729a2a | |||
| 84c22e342c | |||
| 691810c591 | |||
| 67b45d2e05 | |||
| 0576a790dd | |||
| 5e49cd3f95 | |||
| efbe7d167c | |||
| 51b776f393 | |||
| 189d532ac9 | |||
| 3b109d1547 | |||
| 648a790cec | |||
| 1b026d6106 | |||
| 91e18c432c | |||
| 59b6977367 | |||
| c49844feea | |||
| 448b721af5 | |||
| 759b31bce3 | |||
| c76c976cc8 | |||
| 1652df1533 | |||
| b1e1dcdcad | |||
| 47c72dff3e | |||
| 811c6a09c5 | |||
| cb20732205 | |||
| 2aa0f09079 | |||
| 9c35372720 | |||
| b6e68e861b | |||
| b3933b6d63 | |||
| 01327eb8d2 | |||
| 6d080d250d | |||
| 740e14e6cc | |||
| 8d9e4286b0 | |||
| 5403be5e7d | |||
| 1bc36f5e10 | |||
| 8bb0a54f18 | |||
| d03163a189 | |||
| 9875994df8 | |||
| c27b5bd708 | |||
| 4e17ddf638 | |||
| d273932693 | |||
| fadc1e2535 | |||
| c4fb237604 | |||
| 645c92978b | |||
| c50366f670 | |||
| 258e350c35 | |||
| aced495cd6 | |||
| 25e9e07cc8 | |||
| 6cc6d719e1 | |||
| 8cbe313c89 | |||
| 5754a1d94c | |||
| 609aee2513 | |||
| 829f0a6253 | |||
| 0b8b37511e | |||
| 3cc88a5248 | |||
| f814e682cf | |||
| 981e523440 | |||
| 95c2c2e0c1 | |||
| 65a26b1880 | |||
| 077e866915 | |||
| 108d496346 | |||
| 5022a2be1f | |||
| 5d6fd315e3 | |||
| 3722ec8031 | |||
| 441ce2c5ec | |||
| ab9842dc10 | |||
| 56dbb680a7 | |||
| 3a0e72a5c8 | |||
| e2be93565d | |||
| 503296e574 | |||
| 35c2d03602 | |||
| bc97c6bfed | |||
| 4bed3e51b2 | |||
| 8392438dc5 | |||
| 4f730cf58f | |||
| ee8039b301 | |||
| ccea087f6c | |||
| 8706d8c913 | |||
| 1df1bf2e75 | |||
| facbbfe6a1 | |||
| f007e3b207 | |||
| b483415a9a | |||
| aaca5b1723 | |||
| 72d8b45241 | |||
| 17344cdb89 | |||
| 7d527d9a67 | |||
| fdbea5b7e9 | |||
| d995498199 | |||
| e3c0a972fb | |||
| 3c8b91ff6a | |||
| 1c7e13b965 | |||
| b177781fa1 | |||
| 6d9d4ff91b | |||
| ea9cc3cbe4 | |||
| 18c4747de6 | |||
| 4496f2822c | |||
| 200a45a483 | |||
| 25474f851e | |||
| 5d1b642cc8 | |||
| aed665b6b0 | |||
| 29fa23ba36 | |||
| 23b7f66d74 | |||
| d9fe1683d2 | |||
| 3b4de24e1d | |||
| 2342f388c0 | |||
| 5a655ffc9e | |||
| 97c750778a | |||
| 50aac8d7e5 | |||
| f6941fe002 | |||
| 0d6d1db290 | |||
| e1a1b29a0f | |||
| fe549fca4a | |||
| 0606be4711 | |||
| 36d3f81d40 | |||
| 5e019ecf73 | |||
| 6c8568f304 | |||
| e2e21601f4 | |||
| 6b931648dc | |||
| cf53cdfe28 | |||
| 21fb9c7e57 | |||
| f764ad8962 | |||
| 53dbe41d7b | |||
| 17e2c6471a | |||
| 3a030f5bca | |||
| 2070653f2f | |||
| 47b8c5bc65 | |||
| b668f3fbb5 | |||
| 6520f2e9d7 | |||
| 68b97a12a1 | |||
| 1764ff5598 | |||
| 9594d08e40 | |||
| 6b4ef79c45 | |||
| 28a43928aa | |||
| 15b4a9520a | |||
| 8f3daef5cb | |||
| 25d6855b38 | |||
| 08e326d56d | |||
| e711db94c0 | |||
| b6a872b3b8 | |||
| 9e129a1ac0 | |||
| eda5c8dedb | |||
| 557a0d5d3e | |||
| 06f327518a | |||
| db42560654 | |||
| 83db3b2278 | |||
| 05e789b707 | |||
| f5e862ee86 | |||
| fd2ac68a03 | |||
| 806f1cf5ef | |||
| a5d6c81f3a | |||
| 4096499d28 | |||
| bd2d1c6b63 | |||
| 0a8e6793ef | |||
| d6eaf23289 | |||
| df583ef157 | |||
| 3b1d1580a1 | |||
| ca2dedb2e2 | |||
| 769a0432c8 | |||
| 74e42de7aa | |||
| b0eb135f44 | |||
| 056279bdc2 | |||
| 6711f55fba | |||
| e85d4f8ec3 | |||
| 40a892e09d | |||
| 1f2d54d53e | |||
| cb63437e0e | |||
| 88e0fbfb64 | |||
| 3af1b4949f | |||
| 11502cb5f0 | |||
| d94a18779e | |||
| 46c7b96fed | |||
| 3d2c99afaa | |||
| 5a6153cacc | |||
| 810049d62e | |||
| f7a5e4737d | |||
| ceba0c280e | |||
| e6047f6b6e | |||
| dde277c14d | |||
| 8b8c539e06 | |||
| 4a24e1a08d | |||
| db75370873 | |||
| c750ef350d | |||
| 2402c206dc | |||
| 1e949aab69 | |||
| 23343a8558 | |||
| 3271f820d4 | |||
| 8980c29b07 | |||
| 6271dc2e6a | |||
| c9df4fd6f4 | |||
| 2ac3dcda84 | |||
| 8ede415443 | |||
| 1b255090b0 | |||
| 80a35c2f14 | |||
| b7c67ea3c2 | |||
| 264bcf9cb7 | |||
| 621c45f9c0 | |||
| 6131f38232 | |||
| 4980495f90 | |||
| b183ad660f | |||
| 78e885e4ce | |||
| 28cf997f1d | |||
| 417e63fbcb | |||
| 4105d91741 | |||
| 3ecebc0d61 | |||
| 9869897349 | |||
| ff5b40bb56 | |||
| dedb429186 | |||
| 3876162f1d | |||
| d42ca300d3 | |||
| bed87ba9ce | |||
| 6dc6fc427c | |||
| 784b02b571 | |||
| f11bd35b74 | |||
| 164c7a4923 | |||
| a5a4f6cbe2 | |||
| 04242ab3d8 | |||
| 563571d4a5 | |||
| b926f7d6a3 | |||
| 389c2f9846 | |||
| 55d32dad5f | |||
| eb1be3f366 | |||
| 4ddb2c5448 | |||
| 8ceff5c3c7 | |||
| 352c9d9246 | |||
| 67707c0d41 | |||
| 1dbef11a8c | |||
| 69bdc50b3e | |||
| b3322fe367 | |||
| b0ce191aff | |||
| fc0e8a36ce | |||
| 9fc9cda08e | |||
| 287c27a5ab | |||
| bd22e452d3 | |||
| ec2595a751 | |||
| 5e8ed88832 | |||
| 2228d8e879 | |||
| 01b11b6e42 | |||
| cacb9a9b1d | |||
| 16bc3b08bc | |||
| d169f9b9d1 | |||
| fcea68c7c7 | |||
| 2b431377c1 | |||
| 986bdf15a6 | |||
| 32e260ed73 | |||
| 4035172a4b | |||
| 2593097989 | |||
| 6797db914a | |||
| 52669e7055 | |||
| 0884e229fe | |||
| 03a92ce3e9 | |||
| 906d371df6 | |||
| 9e9bf7b8a0 | |||
| fe9b0db985 | |||
| 4a78aa1c20 | |||
| 63db640abe | |||
| 8dfcfbfc18 | |||
| 8acefb2dd3 | |||
| d09fb815bb | |||
| fe25800a42 | |||
| 8f9bbca22b | |||
| 41bbe69442 | |||
| 588c819c3c | |||
| 29975350a6 | |||
| 7f73ea2852 | |||
| e07017b3fc | |||
| 9f72c30e8b | |||
| 5edf802593 | |||
| 5f4ce26b25 | |||
| 196e63f40d | |||
| 1aab2e850a | |||
| 68462960fa | |||
| 9f7dbbd470 | |||
| 1a42f2b858 | |||
| a12019248e | |||
| 76453bb7bd | |||
| cf011f18f2 | |||
| 8704ec477e | |||
| 4cd05c1b9b |
9
.continue/mcpServers/new-mcp-server.yaml
Normal file
9
.continue/mcpServers/new-mcp-server.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
name: New MCP server
|
||||
version: 0.0.1
|
||||
schema: v1
|
||||
mcpServers:
|
||||
- name: chrome-devtools
|
||||
command: node
|
||||
args:
|
||||
- "C:\\nvm4w\\nodejs\\node_modules\\chrome-devtools-mcp\\build\\src\\index.js"
|
||||
env: {}
|
||||
26
.continue/rules/new-rule.md
Normal file
26
.continue/rules/new-rule.md
Normal file
@@ -0,0 +1,26 @@
|
||||
1. **语言与沟通**
|
||||
* 优先用 Go 语言提供所有代码示例。
|
||||
* 使用中文进行回复, 且项目中的注释、报错、枚举、日志等所有文本内容全部使用中文。
|
||||
* 遇到任何不确定或模糊不清的情况,必须直接询问,绝不进行猜测。
|
||||
|
||||
2. **工作流程与方案制定**
|
||||
* 对于所有需求都必须优先输出详尽的文字方案,只有在获得您的明确许可后,才能进行任何代码或文件的修改。
|
||||
* 制定方案时严格遵循两步流程:
|
||||
1. **初步方案**: 基于需求,快速形成一个概要方案。
|
||||
2. **详尽方案**: 通过搜索和阅读所有涉及的代码文件,将初步方案细化为一个不包含任何模糊信息(如“可能需要”、“我需要先查找”等)的、可直接执行的最终方案。
|
||||
* 如果项目根目录存在 `project_structure.txt` 文件,必须查阅该文件以全面了解项目结构,确保在制定方案和修改文件时使用准确的文件路径。
|
||||
|
||||
3. **文件操作与代码修改**
|
||||
* 我将不再使用任何 MCP 服务提供的能力(包括但不限于 `write_file`, `create_new_file`, `replace_text_in_file` 等)来直接修改、新增或删除文件。
|
||||
* 每次提出修改前,我仍会先读取文件的最新内容,并基于最新内容提供修改建议。
|
||||
* 我可以在不征得同意的情况下读取任何我需要分析的文件。
|
||||
* 在需要新建文件时,我将提供文件内容和建议的文件路径,由用户手动创建。
|
||||
|
||||
4. **注释规范**
|
||||
* 积极编写有价值的功能注释、参数注释和逻辑注释。
|
||||
* 绝对禁止添加任何解释性、总结性或礼貌性的“废话”注释(例如:“这段代码修复了问题”,“优化后的代码”,“新增:xxx”,“注入:xxx”等)。
|
||||
* 不得删除或修改用户已有的任何注释,包括但不限于 TODO、FIXME 或文档注释。
|
||||
|
||||
5. **多工具链协同应用策略**
|
||||
* 我知晓并能主动运用下列独立的 MCP 服务,以最高效、最安全的方式完成任务。
|
||||
* **Chrome DevTools MCP 服务 (浏览器自动化)**: 用于所有与前端浏览器相关的任务,包括页面导航、模拟用户交互、检查 DOM 和网络状态等。
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,6 +16,7 @@ vendor/
|
||||
|
||||
# IDE-specific files
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
@@ -23,3 +24,5 @@ vendor/
|
||||
.env
|
||||
|
||||
bin/
|
||||
app_logs/
|
||||
tmp/
|
||||
11
AGENTS.md
Normal file
11
AGENTS.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 资源地址
|
||||
|
||||
1. 你可以访问 http://localhost:8080/ 进入我的前端界面, 前端项目是另一个项目, 但接入的是当前项目对应的后端平台, 如果需要登录账号密码都是huang
|
||||
2. 你可以阅读 config/config.yml 了解我的配置信息, 包括数据库的连接地址和账号密码, 本平台监听的端口等, 后端的swagger界面在 http://localhost:8086/swagger/index.html
|
||||
3. 项目根目录有project_structure.txt, 你需要先阅读此文件了解项目目录结构
|
||||
4. 项目中有config/presets-data目录, 里面有一些预设数据, 可以间接看作数据库内的数据, 在测试环境中他们一般会和数据库的数据保持一致
|
||||
|
||||
# 权限管理
|
||||
|
||||
1. 我授权你执行数据库的所有查询类sql
|
||||
2. 我授权你操作浏览器访问我的项目swagger文档地址和前端项目, 并允许你进行任何操作
|
||||
66
Makefile
66
Makefile
@@ -9,6 +9,15 @@ help:
|
||||
@echo " run Run the application"
|
||||
@echo " build Build the application"
|
||||
@echo " clean Clean generated files"
|
||||
@echo " test Run all tests"
|
||||
@echo " swag Generate Swagger docs"
|
||||
@echo " proto Generate protobuf files"
|
||||
@echo " lint Lint the code"
|
||||
@echo " dev Run in development mode with hot-reload"
|
||||
@echo " mcp-chrome Start the Google Chrome MCP server"
|
||||
@echo " mcp-pgsql Start the PostgreSQL MCP server"
|
||||
@echo " tree Generate the project file structure list"
|
||||
@echo " gemini Start the gemini-cli"
|
||||
@echo " help Show this help message"
|
||||
|
||||
# 运行应用
|
||||
@@ -25,3 +34,60 @@ build:
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f bin/pig-farm-controller
|
||||
|
||||
# 运行所有测试
|
||||
.PHONY: test
|
||||
test:
|
||||
go test --count=1 ./...
|
||||
|
||||
# 生成swagger文档
|
||||
.PHONY: swag
|
||||
swag:
|
||||
if exist docs rmdir /s /q docs
|
||||
swag init -g internal/app/api/api.go --parseInternal --parseDependency
|
||||
|
||||
|
||||
# 生成protobuf文件
|
||||
.PHONY: proto
|
||||
proto:
|
||||
protoc --go_out=internal/infra/transport/proto --go_opt=paths=source_relative --go-grpc_out=internal/infra/transport/proto --go-grpc_opt=paths=source_relative -Iinternal/infra/transport/proto internal/infra/transport/proto/device.proto
|
||||
|
||||
# 运行代码检查
|
||||
.PHONY: lint
|
||||
lint:
|
||||
golangci-lint run ./... -c ./config/.golangci.yml
|
||||
|
||||
# 测试模式(改动文件自动重编译重启)
|
||||
.PHONY: dev
|
||||
dev:
|
||||
air -c ./config/.air.toml
|
||||
|
||||
# 启用谷歌浏览器MCP服务器
|
||||
.PHONY: mcp-chrome
|
||||
mcp-chrome:
|
||||
node "C:\nvm4w\nodejs\node_modules\chrome-devtools-mcp\build\src\index.js"
|
||||
|
||||
# 启用PostgreSQL MCP服务器
|
||||
.PHONY: mcp-pgsql
|
||||
mcp-pgsql:
|
||||
npx mcp-postgres-server "postgresql://pig-farm-controller:pig-farm-controller@192.168.5.16:5431/pig-farm-controller"
|
||||
|
||||
# 生成文件目录树
|
||||
.PHONY: tree
|
||||
|
||||
# 定义要额外排除的生成代码目录
|
||||
EXCLUDE_CONTEXT_PREFIX = internal/infra/transport/lora/chirp_stack_proto/
|
||||
# 最终的文件清单会保存在这里
|
||||
OUTPUT_FILE = project_structure.txt
|
||||
|
||||
# 使用 PowerShell 脚本块执行 Git 命令和二次过滤
|
||||
tree:
|
||||
@powershell -Command "git ls-files --exclude-standard | Select-String -NotMatch '$(EXCLUDE_CONTEXT_PREFIX)' | Out-File -Encoding UTF8 $(OUTPUT_FILE)"
|
||||
@powershell -Command "Add-Content -Path $(OUTPUT_FILE) -Value '$(EXCLUDE_CONTEXT_PREFIX)' -Encoding UTF8"
|
||||
@echo "The project file list has been generated to project_structure.txt"
|
||||
|
||||
# 启用gemini-cli
|
||||
.PHONY: gemini
|
||||
gemini:
|
||||
gemini -m "gemini-2.5-flash"
|
||||
|
||||
|
||||
10
README.md
10
README.md
@@ -1,11 +1,17 @@
|
||||
# 猪场管理系统
|
||||
|
||||
## 安装说明
|
||||
|
||||
### 推荐使用 TimescaleDB
|
||||
|
||||
TimescaleDB 是基于 PostgreSQL 的开源数据库, 专门为处理时序数据而设计的。可以应对后续传海量传感器数据
|
||||
|
||||
## 功能介绍
|
||||
|
||||
### 一. 猪舍控制
|
||||
|
||||
- [ ] 通过猪舍主控操作舍内所有设备(下料口, 风机, 水帘等)
|
||||
- [ ] 通过猪舍主控采集舍内环境数据(温度, 湿度, 氨气浓度等)
|
||||
- [x] 通过猪舍主控采集舍内环境数据(温度, 湿度, 氨气浓度等)
|
||||
- [ ] 监测猪舍主控和舍内设备运行状态
|
||||
- [ ] 根据监测数据自动调整舍内环境
|
||||
- [ ] 环境异常自动报警(微信, 邮件, 短信)
|
||||
@@ -56,4 +62,4 @@
|
||||
|
||||
### 九. RESTful API
|
||||
|
||||
- [ ] 提供RESTful API接口, 方便其他系统对接
|
||||
- [x] 提供RESTful API接口, 方便其他系统对接
|
||||
@@ -1,6 +0,0 @@
|
||||
// TODO 列表
|
||||
|
||||
1. websocket不是安全的wss
|
||||
2. 添加设备时应该激活一下设备状态采集
|
||||
3. 设备Model缺少硬件地址
|
||||
4. 如果同时有两条请求发给同一个设备, 会不会导致接收到错误的回复
|
||||
16
TODO-List.txt
Normal file
16
TODO-List.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
// TODO 列表
|
||||
|
||||
// TODO 可以实现的问题
|
||||
1. plan执行到一半时如果用户删掉里面的task, 后续调度器执行task时可能会找不到这个任务的细节
|
||||
2. 目前调度器把所有任务都当成定时任务了, 手动和限制次数的没做(增加了model对应字段)
|
||||
3. 系统启动时应该检查一遍执行历史库, 将所有显示为执行中的任务都修正为执行失败并报错
|
||||
|
||||
// TODO 暂时实现不了的问题
|
||||
1. 目前设备都只对应一个地址, 但实际上如电磁两位五通阀等设备是需要用两个开关控制的
|
||||
2. Task调度器目前只能一个任务一个任务执行, 但实际上有些任务需要并发执行, 如开启下料口时需要不断从料筒称重传感器读取数据
|
||||
3. ListenHandler 的实现遇到问题只能panic, 没有处理错误
|
||||
4. 暂时不考虑和区域主控间的同步消息, 假设所有消息都是异步的, 这可能导致无法知道指令是否执行成功
|
||||
5. 如果系统停机时间很长, 待执行任务表中的任务过期了怎么办, 目前没有任务过期机制
|
||||
6. 可以用TimescaleDB代替PGSQL, 优化传感器数据存储性能
|
||||
|
||||
已执行次数在停止后需要重置吗
|
||||
37
config.yml
37
config.yml
@@ -1,37 +0,0 @@
|
||||
# 应用配置文件
|
||||
server:
|
||||
# Web服务器监听IP
|
||||
host: "0.0.0.0"
|
||||
# Web服务器监听端口
|
||||
port: 8086
|
||||
# 服务器超时配置(秒)
|
||||
read_timeout: 30
|
||||
write_timeout: 30
|
||||
idle_timeout: 120
|
||||
|
||||
# PostgreSQL数据库配置
|
||||
database:
|
||||
host: "huangwc.com"
|
||||
port: 5432
|
||||
username: "pig-farm-controller"
|
||||
password: "pig-farm-controller"
|
||||
dbname: "pig-farm-controller"
|
||||
sslmode: "disable"
|
||||
# 连接池配置
|
||||
max_open_conns: 5
|
||||
max_idle_conns: 5
|
||||
conn_max_lifetime: 300 # 5分钟
|
||||
|
||||
# WebSocket配置
|
||||
websocket:
|
||||
# WebSocket请求超时时间(秒)
|
||||
timeout: 5
|
||||
# 心跳检测间隔(秒), 如果超过这个时间没有消息往来系统会自动发送一个心跳包维持长链接
|
||||
heartbeat_interval: 54
|
||||
|
||||
# 心跳配置
|
||||
heartbeat:
|
||||
# 心跳间隔(秒)
|
||||
interval: 30
|
||||
# 请求并发数
|
||||
concurrency: 5
|
||||
52
config/.air.toml
Normal file
52
config/.air.toml
Normal file
@@ -0,0 +1,52 @@
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "tmp\\main.exe"
|
||||
cmd = "go build -o ./tmp/main.exe ."
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
silent = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
||||
52
config/.golangci.yml
Normal file
52
config/.golangci.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
# .golangci.yml - 为你的项目量身定制的 linter 配置
|
||||
|
||||
linters-settings:
|
||||
# 这里可以对特定的 linter 进行微调
|
||||
errcheck:
|
||||
# 检查未处理的错误,但可以排除一些常见的、我们确认无需处理的函数
|
||||
exclude-functions:
|
||||
- io/ioutil.ReadFile
|
||||
- io.Copy
|
||||
- io.WriteString
|
||||
- os.Create
|
||||
|
||||
linters:
|
||||
# 明确我们想要禁用的 linter
|
||||
disable:
|
||||
# --- 暂时禁用的“干扰项” ---
|
||||
- godox # 禁用对 TODO, FIXME 注释的检查,让我们能专注于代码
|
||||
|
||||
# --- 暂时禁用的“风格/复杂度”检查器 ---
|
||||
- gocyclo # 暂时不检查圈复杂度
|
||||
- funlen # 暂时不检查函数长度
|
||||
- lll # 暂时不检查行长度
|
||||
- wsl # 检查多余的空格和换行,可以后期再处理
|
||||
- gocritic # 这个检查器包含很多子项,有些可能过于严格,可以先禁用,或在下面精细配置
|
||||
|
||||
# 排除路径:分析这些文件但不报告问题(使用 regex 匹配)
|
||||
exclusions:
|
||||
paths:
|
||||
# 排除 docs/ 目录(匹配路径以 docs/ 开头)
|
||||
- '^docs/'
|
||||
|
||||
# 精细排除规则:用于特定文件/文本的 linter 排除
|
||||
rules:
|
||||
# 排除对 main.go 中 log.Fatalf 的抱怨(仅针对 goconst linter)
|
||||
- path: '^main\.go$'
|
||||
text: "log.Fatalf"
|
||||
linters:
|
||||
- goconst
|
||||
|
||||
# 你也可以明确启用你认为最重要的检查器,形成一个“白名单”
|
||||
# enable:
|
||||
# - govet
|
||||
# - errcheck
|
||||
# - staticcheck
|
||||
# - unused
|
||||
# - gosimple
|
||||
# - ineffassign
|
||||
# - typecheck
|
||||
|
||||
run:
|
||||
# 完全跳过测试文件分析(不解析、不报告任何问题)
|
||||
tests: false
|
||||
135
config/config.example.yml
Normal file
135
config/config.example.yml
Normal file
@@ -0,0 +1,135 @@
|
||||
# 应用基础配置
|
||||
app:
|
||||
name: "PigFarmController" # 应用名称
|
||||
version: "1.0.0" # 应用版本
|
||||
jwt_secret: "your_jwt_secret_key_here" # JWT 签名密钥,请务必修改为强密码
|
||||
|
||||
# 服务器配置
|
||||
server:
|
||||
port: 8080 # 服务器监听端口
|
||||
mode: "debug" # 服务运行模式: debug, release, test
|
||||
|
||||
# 日志配置
|
||||
log:
|
||||
level: "info" # 日志级别: debug, info, warn, error, dpanic, panic, fatal
|
||||
format: "console" # 日志输出格式: console, json
|
||||
enable_file: true # 是否同时输出到文件
|
||||
file_path: "app_logs/pig_farm_controller.log" # 日志文件路径
|
||||
max_size: 100 # 单个日志文件最大大小 (MB)
|
||||
max_backups: 7 # 最多保留的旧日志文件数量
|
||||
max_age: 7 # 最多保留的旧日志文件天数
|
||||
compress: true # 是否压缩旧日志文件
|
||||
enable_trace: false # 是否启用调用链追踪
|
||||
|
||||
# 数据库配置
|
||||
database:
|
||||
host: "localhost" # 数据库主机地址
|
||||
port: 5432 # 数据库端口
|
||||
username: "postgres" # 数据库用户名
|
||||
password: "your_db_password" # 数据库密码
|
||||
dbname: "pig_farm_controller_db" # 数据库名称
|
||||
sslmode: "disable" # SSL模式: disable, require, verify-ca, verify-full
|
||||
is_timescaledb: false # 是否为 TimescaleDB
|
||||
max_open_conns: 100 # 最大开放连接数
|
||||
max_idle_conns: 10 # 最大空闲连接数
|
||||
conn_max_lifetime: 300 # 连接最大生命周期 (秒)
|
||||
|
||||
# WebSocket配置
|
||||
websocket:
|
||||
timeout: 60 # WebSocket请求超时时间 (秒)
|
||||
heartbeat_interval: 30 # 心跳检测间隔 (秒)
|
||||
|
||||
# 心跳配置
|
||||
heartbeat:
|
||||
interval: 10 # 心跳间隔 (秒)
|
||||
concurrency: 5 # 请求并发数
|
||||
|
||||
# ChirpStack API 配置
|
||||
chirp_stack:
|
||||
api_host: "http://localhost:8080" # ChirpStack API 主机地址
|
||||
api_token: "your_chirpstack_api_token" # ChirpStack API Token
|
||||
fport: 10 # ChirpStack FPort
|
||||
api_timeout: 10 # ChirpStack API请求超时时间(秒)
|
||||
# 等待设备上行响应的超时时间(秒)。
|
||||
# 对于LoRaWAN这种延迟较高的网络,建议设置为5分钟 (300秒) 或更长。
|
||||
collection_request_timeout: 300
|
||||
|
||||
# 任务调度配置
|
||||
task:
|
||||
interval: 5 # 任务调度间隔 (秒)
|
||||
num_workers: 5 # 任务执行器并发工作数量
|
||||
|
||||
# Lora 配置
|
||||
lora:
|
||||
mode: "lora_mesh" # Lora 运行模式: lora_wan, lora_mesh
|
||||
|
||||
# Lora Mesh 配置
|
||||
lora_mesh:
|
||||
# 主节点串口
|
||||
uart_port: "COM7"
|
||||
# LoRa模块的通信波特率
|
||||
baud_rate: 9600
|
||||
# 等待LoRa模块AT指令响应的超时时间(ms)
|
||||
timeout: 50
|
||||
# LoRa Mesh 模块发送模式(EC: 透传; ED: 完整数据包)
|
||||
# e.g.
|
||||
# EC: 接收端只会接收到消息, 不会接收到请求头
|
||||
# e.g. 发送: EC 05 02 01 48 65 6c 6c 6f
|
||||
# (EC + 05(消息长度) + 0201(地址) + "Hello"(消息本体))
|
||||
# 接收: 48 65 6c 6c 6f ("Hello")
|
||||
# ED: 接收端会接收完整数据包,包含自定义协议头和地址信息。
|
||||
# e.g. 发送: ED 05 12 34 01 00 01 02 03
|
||||
# (ED(帧头) + 05(Length, 即 1(总包数)+1(当前包序号)+3(数据块)) + 12 34(目标地址) + 01(总包数) + 00(当前包序号) + 01 02 03(数据块))
|
||||
# 接收: ED 05 12 34 01 00 01 02 03 56 78(56 78 是发送方地址,会自动拼接到消息末尾)
|
||||
lora_mesh_mode: "ED"
|
||||
# 单包最大用户数据数据长度, 模块限制240, 去掉两位自定义包头, 还剩238
|
||||
max_chunk_size: 238
|
||||
#分片重组超时时间(秒)。如果在一个分片到达后,超过这个时间
|
||||
# 还没收到完整的包,则认为接收失败。
|
||||
reassembly_timeout: 30
|
||||
|
||||
# 通知服务配置
|
||||
notify:
|
||||
primary: "日志" # 首选通知渠道: "邮件", "企业微信", "飞书", "日志" (如果其他渠道未启用,"日志" 会自动成为首选)
|
||||
failureThreshold: 2 # 连续失败多少次后触发广播模式
|
||||
smtp:
|
||||
enabled: false # 是否启用 SMTP 邮件通知
|
||||
host: "smtp.example.com" # SMTP 服务器地址
|
||||
port: 587 # SMTP 服务器端口
|
||||
username: "your_email@example.com" # 发件人邮箱地址
|
||||
password: "your_email_password" # 发件人邮箱授权码或密码
|
||||
sender: "PigFarm Alarm <no-reply@example.com>" # 发件人名称和地址
|
||||
|
||||
wechat:
|
||||
enabled: false # 是否启用企业微信通知
|
||||
corpID: "wwxxxxxxxxxxxx" # 企业ID (CorpID)
|
||||
agentID: "1000001" # 应用ID (AgentID)
|
||||
secret: "your_wechat_app_secret" # 应用密钥 (Secret)
|
||||
|
||||
lark:
|
||||
enabled: false # 是否启用飞书通知
|
||||
appID: "cli_xxxxxxxxxx" # 应用 ID
|
||||
appSecret: "your_lark_app_secret" # 应用密钥
|
||||
|
||||
# 定时采集配置
|
||||
collection:
|
||||
interval: 1 # 采集间隔 (分钟)
|
||||
|
||||
# 告警通知配置
|
||||
alarm_notification:
|
||||
notification_intervals: # 告警通知间隔(分钟)
|
||||
debug: 1
|
||||
info: 1
|
||||
warn: 1
|
||||
error: 1
|
||||
dpanic: 1
|
||||
panic: 1
|
||||
fatal: 1
|
||||
|
||||
# AI 服务配置
|
||||
ai:
|
||||
model: "gemini" # 不指定就是不用AI
|
||||
gemini:
|
||||
api_key: "YOUR_GEMINI_API_KEY" # 替换为你的 Gemini API Key
|
||||
model_name: "gemini-2.5-flash" # Gemini 模型名称,例如 "gemini-pro"
|
||||
timeout: 30 # AI 请求超时时间 (秒)
|
||||
113
config/config.yml
Normal file
113
config/config.yml
Normal file
@@ -0,0 +1,113 @@
|
||||
# 应用基础配置
|
||||
app:
|
||||
name: "pig-farm-controller"
|
||||
version: "1.0.0"
|
||||
# JWT 密钥,用于签发和验证 token。请在生产环境中替换为更复杂的密钥!
|
||||
jwt_secret: "pig-farm-controller"
|
||||
|
||||
# HTTP 服务配置
|
||||
server:
|
||||
port: 8086
|
||||
mode: "release" # 服务运行模式: "debug", "release", "test"
|
||||
|
||||
# 日志配置
|
||||
log:
|
||||
level: "info" # 日志级别: "debug", "info", "warn", "error", "dpanic", "panic", "fatal"
|
||||
format: "console" # 日志格式: "console" 或 "json"
|
||||
enable_file: true # 是否启用文件日志
|
||||
file_path: "./app_logs/app.log" # 日志文件路径
|
||||
max_size: 10 # 每个日志文件的最大尺寸 (MB)
|
||||
max_backups: 5 # 保留的旧日志文件的最大数量
|
||||
max_age: 30 # 保留的旧日志文件的最大天数
|
||||
compress: false # 是否压缩/归档旧日志文件
|
||||
enable_trace: true # 是否启用调用链追踪
|
||||
|
||||
# 数据库配置 (PostgreSQL)
|
||||
database:
|
||||
host: "192.168.5.16"
|
||||
port: 5431
|
||||
username: "pig-farm-controller"
|
||||
password: "pig-farm-controller"
|
||||
dbname: "pig-farm-controller"
|
||||
sslmode: "disable" # 在生产环境中建议使用 "require"
|
||||
is_timescaledb: true
|
||||
max_open_conns: 25 # 最大开放连接数
|
||||
max_idle_conns: 10 # 最大空闲连接数
|
||||
conn_max_lifetime: 600 # 连接最大生命周期(秒)
|
||||
|
||||
# WebSocket 配置 (如果使用)
|
||||
websocket:
|
||||
timeout: 60 # WebSocket请求超时时间(秒)
|
||||
heartbeat_interval: 30 # 心跳检测间隔(秒)
|
||||
|
||||
# 心跳/定时任务配置
|
||||
heartbeat:
|
||||
interval: 600 # 任务调度或心跳检查的默认间隔(秒)
|
||||
concurrency: 2 # 执行任务的并发协程数
|
||||
|
||||
# chirp_stack 配置文件
|
||||
chirp_stack:
|
||||
api_host: "http://192.168.5.16:8090" # ChirpStack API服务器地址
|
||||
api_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJjaGlycHN0YWNrIiwiaXNzIjoiY2hpcnBzdGFjayIsInN1YiI6IjU2ZWRhNWQ3LTM4NzgtNDAwMC05MWMzLWYwZDk3M2YwODhjNiIsInR5cCI6ImtleSJ9.NxBxTrhPAnezKMqAYZR_Uq2mGQjJRlmVzg1ZDFCyaHQ" # ChirpStack API密钥, 请求头中需要设置 Grpc-Metadata-Authorization: Bearer <YOUR_API_TOKEN>
|
||||
fport: 1
|
||||
api_timeout: 10 # ChirpStack API请求超时时间(秒)
|
||||
# 等待设备上行响应的超时时间(秒)。
|
||||
# 对于LoRaWAN这种延迟较高的网络,建议设置为5分钟 (300秒) 或更长。
|
||||
collection_request_timeout: 300
|
||||
|
||||
|
||||
# 任务调度器配置
|
||||
task:
|
||||
interval: 3
|
||||
num_workers: 5
|
||||
|
||||
# Lora 配置
|
||||
lora:
|
||||
mode: "lora_mesh" # "lora_wan" or "lora_mesh"
|
||||
|
||||
lora_mesh:
|
||||
# 主节点串口
|
||||
uart_port: "COM7"
|
||||
# LoRa模块的通信波特率
|
||||
baud_rate: 9600
|
||||
# 等待LoRa模块AT指令响应的超时时间(ms)
|
||||
timeout: 50
|
||||
# LoRa Mesh 模块发送模式(EC: 透传; ED: 完整数据包)
|
||||
# e.g.
|
||||
# EC: 接收端只会接收到消息, 不会接收到请求头
|
||||
# e.g. 发送: EC 05 02 01 48 65 6c 6c 6f
|
||||
# (EC + 05(消息长度) + 0201(地址) + "Hello"(消息本体))
|
||||
# 接收: 48 65 6c 6c 6f ("Hello")
|
||||
# ED: 接收端会接收完整数据包,包含自定义协议头和地址信息。
|
||||
# e.g. 发送: ED 05 12 34 01 00 01 02 03
|
||||
# (ED(帧头) + 05(Length, 即 1(总包数)+1(当前包序号)+3(数据块)) + 12 34(目标地址) + 01(总包数) + 00(当前包序号) + 01 02 03(数据块))
|
||||
# 接收: ED 05 12 34 01 00 01 02 03 56 78(56 78 是发送方地址,会自动拼接到消息末尾)
|
||||
lora_mesh_mode: "ED"
|
||||
# 单包最大用户数据数据长度, 模块限制240, 去掉两位自定义包头, 还剩238
|
||||
max_chunk_size: 238
|
||||
#分片重组超时时间(秒)。如果在一个分片到达后,超过这个时间
|
||||
# 还没收到完整的包,则认为接收失败。
|
||||
reassembly_timeout: 30
|
||||
|
||||
# 定时采集配置
|
||||
collection:
|
||||
interval: 1 # 采集间隔 (分钟)
|
||||
|
||||
# 告警通知配置
|
||||
alarm_notification:
|
||||
notification_intervals: # 告警通知间隔 (分钟)
|
||||
debug: 1
|
||||
info: 1
|
||||
warn: 1
|
||||
error: 1
|
||||
dpanic: 1
|
||||
panic: 1
|
||||
fatal: 1
|
||||
|
||||
# AI 服务配置
|
||||
ai:
|
||||
model: Gemini
|
||||
gemini:
|
||||
api_key: "AIzaSyAJdXUmoN07LIswDac6YxPeRnvXlR73OO8" # 替换为你的 Gemini API Key
|
||||
model_name: "gemini-2.0-flash" # Gemini 模型名称,例如 "gemini-pro"
|
||||
timeout: 30 # AI 请求超时时间 (秒)
|
||||
2206
config/presets-data/nutrient.json
Normal file
2206
config/presets-data/nutrient.json
Normal file
File diff suppressed because it is too large
Load Diff
914
config/presets-data/pig_nutrient_requirement.json
Normal file
914
config/presets-data/pig_nutrient_requirement.json
Normal file
@@ -0,0 +1,914 @@
|
||||
{
|
||||
"type": "pig_nutrient_requirements",
|
||||
"data": {
|
||||
"杜长大 (DLY)": {
|
||||
"保育期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 1.2,
|
||||
"max_requirement": 1.5
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.72,
|
||||
"max_requirement": 1.05
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.78,
|
||||
"max_requirement": 1.08
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.22,
|
||||
"max_requirement": 0.30
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 18.0,
|
||||
"max_requirement": 22.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.9,
|
||||
"max_requirement": 1.2
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.6,
|
||||
"max_requirement": 0.8
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.2,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 3226.5,
|
||||
"max_requirement": 3585.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"育肥前期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.94,
|
||||
"max_requirement": 1.10
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.55,
|
||||
"max_requirement": 0.73
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.58,
|
||||
"max_requirement": 0.77
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.16,
|
||||
"max_requirement": 0.22
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 16.0,
|
||||
"max_requirement": 18.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.7,
|
||||
"max_requirement": 0.9
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.5,
|
||||
"max_requirement": 0.7
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.2,
|
||||
"max_requirement": 0.40
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 3107.0,
|
||||
"max_requirement": 3346.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"育肥后期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.81,
|
||||
"max_requirement": 0.90
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.45,
|
||||
"max_requirement": 0.58
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.48,
|
||||
"max_requirement": 0.61
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.13,
|
||||
"max_requirement": 0.18
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 14.0,
|
||||
"max_requirement": 16.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.6,
|
||||
"max_requirement": 0.8
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.45,
|
||||
"max_requirement": 0.6
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.18,
|
||||
"max_requirement": 0.35
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 2987.5,
|
||||
"max_requirement": 3226.5
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"二次育肥期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.53,
|
||||
"max_requirement": 0.65
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.30,
|
||||
"max_requirement": 0.41
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.31,
|
||||
"max_requirement": 0.43
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.10,
|
||||
"max_requirement": 0.13
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 12.0,
|
||||
"max_requirement": 14.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.5,
|
||||
"max_requirement": 0.7
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.4,
|
||||
"max_requirement": 0.55
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.30
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 2868.0,
|
||||
"max_requirement": 3107.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
}
|
||||
},
|
||||
"杜大长 (DYL)": {
|
||||
"保育期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 1.2,
|
||||
"max_requirement": 1.5
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.72,
|
||||
"max_requirement": 1.05
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.78,
|
||||
"max_requirement": 1.08
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.22,
|
||||
"max_requirement": 0.30
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 18.0,
|
||||
"max_requirement": 22.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.9,
|
||||
"max_requirement": 1.2
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.6,
|
||||
"max_requirement": 0.8
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.2,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 3226.5,
|
||||
"max_requirement": 3585.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"育肥前期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.94,
|
||||
"max_requirement": 1.10
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.55,
|
||||
"max_requirement": 0.73
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.58,
|
||||
"max_requirement": 0.77
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.16,
|
||||
"max_requirement": 0.22
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 16.0,
|
||||
"max_requirement": 18.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.7,
|
||||
"max_requirement": 0.9
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.5,
|
||||
"max_requirement": 0.7
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.2,
|
||||
"max_requirement": 0.40
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 3107.0,
|
||||
"max_requirement": 3346.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"育肥后期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.81,
|
||||
"max_requirement": 0.90
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.45,
|
||||
"max_requirement": 0.58
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.48,
|
||||
"max_requirement": 0.61
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.13,
|
||||
"max_requirement": 0.18
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 14.0,
|
||||
"max_requirement": 16.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.6,
|
||||
"max_requirement": 0.8
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.45,
|
||||
"max_requirement": 0.6
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.18,
|
||||
"max_requirement": 0.35
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 2987.5,
|
||||
"max_requirement": 3226.5
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"二次育肥期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.53,
|
||||
"max_requirement": 0.65
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.30,
|
||||
"max_requirement": 0.41
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.31,
|
||||
"max_requirement": 0.43
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.10,
|
||||
"max_requirement": 0.13
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 12.0,
|
||||
"max_requirement": 14.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.5,
|
||||
"max_requirement": 0.7
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.4,
|
||||
"max_requirement": 0.55
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.30
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 2868.0,
|
||||
"max_requirement": 3107.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
}
|
||||
},
|
||||
"皮长大 (PLY)": {
|
||||
"保育期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 1.2,
|
||||
"max_requirement": 1.5
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.72,
|
||||
"max_requirement": 1.05
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.78,
|
||||
"max_requirement": 1.08
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.22,
|
||||
"max_requirement": 0.30
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 18.0,
|
||||
"max_requirement": 22.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.9,
|
||||
"max_requirement": 1.2
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.6,
|
||||
"max_requirement": 0.8
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.2,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 3226.5,
|
||||
"max_requirement": 3585.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"育肥前期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.94,
|
||||
"max_requirement": 1.10
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.55,
|
||||
"max_requirement": 0.73
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.58,
|
||||
"max_requirement": 0.77
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.16,
|
||||
"max_requirement": 0.22
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 16.0,
|
||||
"max_requirement": 18.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.7,
|
||||
"max_requirement": 0.9
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.5,
|
||||
"max_requirement": 0.7
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.2,
|
||||
"max_requirement": 0.40
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 3107.0,
|
||||
"max_requirement": 3346.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"育肥后期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.81,
|
||||
"max_requirement": 0.90
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.45,
|
||||
"max_requirement": 0.58
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.48,
|
||||
"max_requirement": 0.61
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.13,
|
||||
"max_requirement": 0.18
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 14.0,
|
||||
"max_requirement": 16.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.6,
|
||||
"max_requirement": 0.8
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.45,
|
||||
"max_requirement": 0.6
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.18,
|
||||
"max_requirement": 0.35
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 2987.5,
|
||||
"max_requirement": 3226.5
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
},
|
||||
"二次育肥期": {
|
||||
"可消化赖氨酸 (SID %)": {
|
||||
"min_requirement": 0.53,
|
||||
"max_requirement": 0.65
|
||||
},
|
||||
"蛋+胱氨酸 (%)": {
|
||||
"min_requirement": 0.30,
|
||||
"max_requirement": 0.41
|
||||
},
|
||||
"可消化苏氨酸 (SID %)": {
|
||||
"min_requirement": 0.31,
|
||||
"max_requirement": 0.43
|
||||
},
|
||||
"可消化色氨酸 (SID %)": {
|
||||
"min_requirement": 0.10,
|
||||
"max_requirement": 0.13
|
||||
},
|
||||
"粗蛋白 (%)": {
|
||||
"min_requirement": 12.0,
|
||||
"max_requirement": 14.0
|
||||
},
|
||||
"粗脂肪 (%)": {
|
||||
"min_requirement": 3.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"粗纤维 (%)": {
|
||||
"min_requirement": 2.0,
|
||||
"max_requirement": 6.0
|
||||
},
|
||||
"钙 (%)": {
|
||||
"min_requirement": 0.5,
|
||||
"max_requirement": 0.7
|
||||
},
|
||||
"总磷 (%)": {
|
||||
"min_requirement": 0.4,
|
||||
"max_requirement": 0.55
|
||||
},
|
||||
"有效磷 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.30
|
||||
},
|
||||
"代谢能 (kcal/kg)": {
|
||||
"min_requirement": 2868.0,
|
||||
"max_requirement": 3107.0
|
||||
},
|
||||
"钠 (%)": {
|
||||
"min_requirement": 0.15,
|
||||
"max_requirement": 0.25
|
||||
},
|
||||
"氯 (%)": {
|
||||
"min_requirement": 0.25,
|
||||
"max_requirement": 0.45
|
||||
},
|
||||
"黄曲霉毒素B1 (μg/kg)": {
|
||||
"max_requirement": 10
|
||||
},
|
||||
"呕吐毒素DON (μg/kg)": {
|
||||
"max_requirement": 1
|
||||
},
|
||||
"玉米赤霉烯酮ZEN (μg/kg)": {
|
||||
"max_requirement": 0.15
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"descriptions": {
|
||||
"pig_breeds": {
|
||||
"杜长大 (DLY)": {
|
||||
"description": "杜长大是中国市场占有率最高的商品肉猪。通过利用杜洛克、长白、大约克三品种的杂种优势,实现高效生长和高瘦肉率。",
|
||||
"parent_info": "终端父本:杜洛克 (D);二元母本:长白 (L) × 大约克 (Y)。",
|
||||
"appearance_features": "全身白色,体型健壮、体躯较长,肌肉发达,背腰平直。",
|
||||
"breed_advantages": "生长速度快、日增重极高、饲料转化率最优、瘦肉率稳定在60%以上、适应性良好、出栏时间最短。",
|
||||
"breed_disadvantages": "抗应激能力中等,对疫病和环境变化相对敏感;无法作为种猪进行自繁。"
|
||||
},
|
||||
"杜大长 (DYL)": {
|
||||
"description": "杜大长是另一种重要的外三元猪,与杜长大体系相似,但在母本的搭配上有所区别,同样追求高生长速度和高瘦肉率。",
|
||||
"parent_info": "终端父本:杜洛克 (D);二元母本:大约克 (Y) × 长白 (L)。",
|
||||
"appearance_features": "全身白色,体型比杜长大略微魁梧,肌肉丰满度高。",
|
||||
"breed_advantages": "生长性能和瘦肉率与杜长大相当,同时遗传了大约克母本的良好体型和生长潜力,综合性能优秀。",
|
||||
"breed_disadvantages": "与杜长大相似,无法留作种用,需要依赖稳定的种源体系;对饲养管理要求高。"
|
||||
},
|
||||
"皮长大 (PLY)": {
|
||||
"description": "皮长大是以皮特兰作为终端父本的杂交体系,专注于生产超高瘦肉率的商品肉猪。",
|
||||
"parent_info": "终端父本:皮特兰 (P);二元母本:长白 (L) × 大约克 (Y)。",
|
||||
"appearance_features": "多数为白色或带有黑色斑点,肌肉极其发达,后臀饱满,体型呈方形。",
|
||||
"breed_advantages": "瘦肉率极高(能达到65%以上),胴体丰满,背膘薄。",
|
||||
"breed_disadvantages": "生长速度和日增重逊于杜长大;应激敏感性极高(易发生P.S.S.),管理难度大,肉质易出现PSE(苍白、软、渗水)现象,影响口感和加工性能。"
|
||||
}
|
||||
},
|
||||
"pig_age_stages": {
|
||||
"保育期": "从断奶到转入生长舍。主要目标是适应固体饲料,建立肠道菌群,确保健康稳定过渡,体重约5kg~30kg。",
|
||||
"育肥前期": "小猪转入育肥舍后到体重达到约60kg的阶段。以骨骼和肌肉生长为主,是高效增重期。",
|
||||
"育肥后期": "体重从约60kg到达到出栏体重(约110-120kg)的阶段。脂肪沉积速度开始加快,是出栏前的冲刺期。",
|
||||
"二次育肥期": "指收购达到常规出栏体重(约100-120kg)的商品猪,继续饲养至更大体重(140-180kg+)的阶段。其存在主要受市场价格波动驱动,目的是提高单体出肉量。"
|
||||
},
|
||||
"pig_breed_age_stages": {
|
||||
"杜长大 (DLY)": {
|
||||
"保育期": {
|
||||
"description": "遗传自杜洛克的生长优势在断奶后开始显现,需精细化管理以避免断奶应激和腹泻。",
|
||||
"daily_feed_intake": 400.0,
|
||||
"daily_gain_weight": 350.0,
|
||||
"min_days": 21,
|
||||
"max_days": 70,
|
||||
"min_weight": 5000.0,
|
||||
"max_weight": 30000.0
|
||||
},
|
||||
"育肥前期": {
|
||||
"description": "生长速度和饲料转化率表现良好,是瘦肉沉积效率高的阶段。",
|
||||
"daily_feed_intake": 1800.0,
|
||||
"daily_gain_weight": 700.0,
|
||||
"min_days": 71,
|
||||
"max_days": 120,
|
||||
"min_weight": 30000.0,
|
||||
"max_weight": 60000.0
|
||||
},
|
||||
"育肥后期": {
|
||||
"description": "继续实现较高日增重,脂肪沉积开始加快,管理目标为达到目标出栏体重并保持料肉比。",
|
||||
"daily_feed_intake": 2800.0,
|
||||
"daily_gain_weight": 800.0,
|
||||
"min_days": 121,
|
||||
"max_days": 180,
|
||||
"min_weight": 60000.0,
|
||||
"max_weight": 100000.0
|
||||
},
|
||||
"二次育肥期": {
|
||||
"description": "增重效率下降,料肉比恶化,增重多为脂肪沉积,注意热应激与蹄部问题。",
|
||||
"daily_feed_intake": 3500.0,
|
||||
"daily_gain_weight": 600.0,
|
||||
"min_days": 181,
|
||||
"max_days": 240,
|
||||
"min_weight": 100000.0,
|
||||
"max_weight": 140000.0
|
||||
}
|
||||
},
|
||||
"杜大长 (DYL)": {
|
||||
"保育期": {
|
||||
"description": "与杜长大相近,生长潜力强,管理重点为断奶适应与稳定采食。",
|
||||
"daily_feed_intake": 400.0,
|
||||
"daily_gain_weight": 330.0,
|
||||
"min_days": 21,
|
||||
"max_days": 70,
|
||||
"min_weight": 5000.0,
|
||||
"max_weight": 30000.0
|
||||
},
|
||||
"育肥前期": {
|
||||
"description": "生长期增重与料肉比接近杜长大,肌肉发展迅速。",
|
||||
"daily_feed_intake": 1750.0,
|
||||
"daily_gain_weight": 680.0,
|
||||
"min_days": 71,
|
||||
"max_days": 120,
|
||||
"min_weight": 30000.0,
|
||||
"max_weight": 60000.0
|
||||
},
|
||||
"育肥后期": {
|
||||
"description": "保持较高增重速度,脂肪沉积稍快于部分 DLY 群体,需配方微调以控制背膘。",
|
||||
"daily_feed_intake": 2700.0,
|
||||
"daily_gain_weight": 770.0,
|
||||
"min_days": 121,
|
||||
"max_days": 180,
|
||||
"min_weight": 60000.0,
|
||||
"max_weight": 100000.0
|
||||
},
|
||||
"二次育肥期": {
|
||||
"description": "与杜长大相似,采食量高但增重多为脂肪,注意健康与福利管理。",
|
||||
"daily_feed_intake": 3500.0,
|
||||
"daily_gain_weight": 580.0,
|
||||
"min_days": 181,
|
||||
"max_days": 240,
|
||||
"min_weight": 100000.0,
|
||||
"max_weight": 140000.0
|
||||
}
|
||||
},
|
||||
"皮长大 (PLY)": {
|
||||
"保育期": {
|
||||
"description": "个体应激敏感性可能更高,需要稳定环境与逐步换料以减少应激性下降重。",
|
||||
"daily_feed_intake": 350.0,
|
||||
"daily_gain_weight": 300.0,
|
||||
"min_days": 21,
|
||||
"max_days": 70,
|
||||
"min_weight": 5000.0,
|
||||
"max_weight": 30000.0
|
||||
},
|
||||
"育肥前期": {
|
||||
"description": "增重速度一般,但瘦肉率高;需注意高应激个体的管理以避免肉质问题。",
|
||||
"daily_feed_intake": 1600.0,
|
||||
"daily_gain_weight": 600.0,
|
||||
"min_days": 71,
|
||||
"max_days": 120,
|
||||
"min_weight": 30000.0,
|
||||
"max_weight": 60000.0
|
||||
},
|
||||
"育肥后期": {
|
||||
"description": "瘦肉率高且脂肪沉积较慢,但应激易导致肉质问题,育肥管理需谨慎。",
|
||||
"daily_feed_intake": 2400.0,
|
||||
"daily_gain_weight": 650.0,
|
||||
"min_days": 121,
|
||||
"max_days": 180,
|
||||
"min_weight": 60000.0,
|
||||
"max_weight": 100000.0
|
||||
},
|
||||
"二次育肥期": {
|
||||
"description": "超重育肥带来的应激和死亡风险增加,通常不推荐长期二次育肥。",
|
||||
"daily_feed_intake": 3200.0,
|
||||
"daily_gain_weight": 450.0,
|
||||
"min_days": 181,
|
||||
"max_days": 240,
|
||||
"min_weight": 100000.0,
|
||||
"max_weight": 140000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# 任务接口增加获取关联设备ID方法设计
|
||||
|
||||
## 1. 需求
|
||||
|
||||
为了在设备删除前进行验证,需要为任务接口增加一个方法,该方法能够直接返回指定任务配置中所有关联的设备ID列表。所有实现 `task` 接口的对象都必须实现此方法。
|
||||
|
||||
## 2. 新接口定义:`TaskDeviceIDResolver`
|
||||
|
||||
```go
|
||||
// TaskDeviceIDResolver 定义了从任务配置中解析设备ID的方法
|
||||
type TaskDeviceIDResolver interface {
|
||||
// ResolveDeviceIDs 从任务配置中解析并返回所有关联的设备ID列表
|
||||
// 返回值: uint数组,每个字符串代表一个设备ID
|
||||
ResolveDeviceIDs() ([]uint, error)
|
||||
}
|
||||
```
|
||||
|
||||
## 3. `task` 接口更新
|
||||
|
||||
`task` 接口将嵌入 `TaskDeviceIDResolver` 接口。
|
||||
|
||||
```go
|
||||
// Task 接口(示例,具体结构可能不同)
|
||||
type Task interface {
|
||||
// ... 其他现有方法 ...
|
||||
|
||||
// 嵌入 TaskDeviceIDResolver 接口
|
||||
TaskDeviceIDResolver
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 实现要求
|
||||
|
||||
所有当前及未来实现 `Task` 接口的类型,都必须实现 `TaskDeviceIDResolver` 接口中定义的所有方法,即 `ResolveDeviceIDs` 方法。
|
||||
@@ -0,0 +1,41 @@
|
||||
# 方案:删除设备前的使用校验
|
||||
|
||||
## 1. 目标
|
||||
|
||||
在删除设备前,检查该设备是否被任何任务关联。如果设备正在被使用,则禁止删除,并向用户返回明确的错误提示。
|
||||
|
||||
## 2. 核心思路
|
||||
|
||||
我们将遵循您项目清晰的分层架构,将“检查设备是否被任务使用”这一业务规则放在 **应用层** (`internal/app/service/`)
|
||||
中进行协调。当上层请求删除设备时,应用服务会先调用仓库层查询 `device_tasks` 关联表,如果发现设备仍被任务关联,则会拒绝删除并返回一个明确的业务错误。
|
||||
|
||||
## 3. 实施步骤
|
||||
|
||||
### 3.1. 仓库层 (`DeviceRepository`)
|
||||
|
||||
- **动作**: 在 `internal/infra/repository/device_repository.go` 的 `DeviceRepository` 接口中,增加一个新方法
|
||||
`IsDeviceInUse(deviceID uint) (bool, error)`。
|
||||
- **实现**: 在 `gormDeviceRepository` 中实现此方法。该方法将通过对 `models.DeviceTask` 模型执行 `Count`
|
||||
操作来高效地判断是否存在 `device_id` 匹配的记录。这比查询完整记录性能更好。
|
||||
|
||||
### 3.2. 应用层 (`DeviceService`)
|
||||
|
||||
- **动作**:
|
||||
1. 在 `internal/app/service/device_service.go` 文件顶部定义一个新的错误变量 `ErrDeviceInUse`,例如
|
||||
`var ErrDeviceInUse = errors.New("设备正在被一个或多个任务使用,无法删除")`。
|
||||
2. 修改该文件中的 `DeleteDevice` 方法。
|
||||
- **实现**: 在 `DeleteDevice` 方法中,在调用 `s.deviceRepo.Delete()` 之前,先调用我们刚刚创建的
|
||||
`s.deviceRepo.IsDeviceInUse()` 方法。如果返回 `true`,则立即返回 `ErrDeviceInUse` 错误,中断删除流程。
|
||||
|
||||
### 3.3. 表现层 (`DeviceController`)
|
||||
|
||||
- **动作**: 修改 `internal/app/controller/device/device_controller.go` 中的 `DeleteDevice` 方法。
|
||||
- **实现**: 在错误处理逻辑中,增加一个 `case` 来专门捕获从服务层返回的 `service.ErrDeviceInUse`
|
||||
错误。当捕获到此错误时,返回一个带有明确提示信息(如“设备正在被任务使用,无法删除”)和合适 HTTP 状态码(例如 `409 Conflict`)的错误响应。
|
||||
|
||||
## 4. 方案优势
|
||||
|
||||
- **职责清晰**: 业务流程的编排和校验逻辑被正确地放置在应用层,符合您项目清晰的分层架构。
|
||||
- **高效查询**: 通过 `COUNT` 查询代替 `Find`,避免了不必要的数据加载,性能更佳。
|
||||
- **代码内聚**: 与设备相关的数据库操作都统一封装在 `DeviceRepository` 中。
|
||||
- **用户友好**: 通过在控制器层处理特定业务错误,可以给前端返回明确、可操作的错误信息。
|
||||
@@ -0,0 +1,111 @@
|
||||
# 方案:维护设备与任务的关联关系
|
||||
|
||||
## 1. 目标
|
||||
|
||||
在对计划(Plan)及其包含的任务(Task)进行创建、更新、删除(CRUD)操作时,同步维护 `device_tasks` 这张多对多关联表。
|
||||
|
||||
这是实现“删除设备前检查其是否被任务使用”这一需求的基础。
|
||||
|
||||
## 2. 核心挑战
|
||||
|
||||
1. **参数结构异构性**:不同类型的任务(`TaskType`),其设备 ID 存储在 `Parameters` (JSON) 字段中的 `key` 和数据结构(单个 ID
|
||||
或 ID 数组)各不相同。
|
||||
2. **分层架构原则**:解析 `Parameters` 以提取设备 ID 的逻辑属于 **业务规则**,需要找到一个合适的位置来封装它,以维持各层职责的清晰。
|
||||
|
||||
## 3. 方案设计
|
||||
|
||||
本方案旨在最大化地复用现有领域模型和逻辑,通过扩展 `TaskFactory` 来实现设备ID的解析,从而保持了各领域模块的高内聚和低耦合。
|
||||
|
||||
### 3.1. 核心思路:复用领域对象与工厂
|
||||
|
||||
我们不移动任何结构体,也不在 `plan` 包中引入任何具体任务的实现细节。取而代之,我们利用现有的 `TaskFactory`
|
||||
和各个任务领域对象自身的能力来解析参数。
|
||||
|
||||
每个具体的任务领域对象(如 `ReleaseFeedWeightTask`)最了解如何解析自己的 `Parameters`。因此,我们将解析设备ID的责任完全交还给它们。
|
||||
|
||||
### 3.2. 扩展 `TaskFactory`
|
||||
|
||||
- **动作**:在 `plan.TaskFactory` 接口中增加一个新方法 `CreateTaskFromModel(*models.Task) (TaskDeviceIDResolver, error)`。
|
||||
- **目的**:此方法允许我们在非任务执行的场景下(例如,在增删改查计划时),仅根据数据库模型 `models.Task` 来创建一个临时的、轻量级的任务领域对象。
|
||||
- **实现**:在 `internal/domain/task/task.go` 的 `taskFactory` 中实现此方法。它会根据传入的 `taskModel.Type`,`switch-case`
|
||||
来调用相应的构造函数(如 `NewReleaseFeedWeightTask`)创建实例。
|
||||
- **实现**:
|
||||
- **优势**:
|
||||
- **高内聚,低耦合**:`plan` 包保持通用,无需了解任何具体任务的参数细节。参数定义和解析逻辑都保留在各自的 `task` 包内。
|
||||
- **逻辑复用**:完美复用了您已在 `ReleaseFeedWeightTask` 中实现的 `ResolveDeviceIDs` 方法,避免了重复代码。
|
||||
|
||||
### 3.3. 调整领域服务层 (`PlanService`)
|
||||
|
||||
`PlanService` 将作为此业务用例的核心编排者。借助 `UnitOfWork` 模式,它可以在单个事务中协调多个仓库,完成数据准备和持久化。
|
||||
|
||||
- **职责**:在创建或更新计划的业务流程中,负责解析任务参数、准备设备关联数据,并调用仓库层完成持久化。
|
||||
- **实现**:
|
||||
- 向 `planServiceImpl` 注入 `repository.UnitOfWork` 和 `plan.TaskFactory`。
|
||||
- 在 `CreatePlan` 和 `UpdatePlan` 方法中,使用 `unitOfWork.ExecuteInTransaction` 来包裹整个操作。
|
||||
- 在事务闭包内,遍历计划中的所有任务 (`models.Task`):
|
||||
1. 调用 `taskFactory.CreateTaskFromModel(taskModel)` 创建一个临时的任务领域对象。
|
||||
2. 调用该领域对象的 `ResolveDeviceIDs()` 方法获取设备ID列表。
|
||||
3. 使用事务性的 `DeviceRepository` 查询出设备实体。
|
||||
4. 将查询到的设备实体列表填充到 `taskModel.Devices` 字段中。
|
||||
- 最后,将填充好关联数据的 `plan` 对象传递给事务性的 `PlanRepository` 进行创建或更新。
|
||||
- **优势**:
|
||||
- **职责清晰**:`PlanService` 完整地拥有了“创建/更新计划”的业务逻辑,而仓库层则回归到纯粹的数据访问职责。
|
||||
- **数据一致性**:`UnitOfWork` 确保了从准备数据(查询设备)到最终持久化(创建计划和关联)的所有数据库操作都在一个原子事务中完成。
|
||||
|
||||
### 3.4. 调整仓库层 (`PlanRepository`)
|
||||
|
||||
仓库层被简化,回归其作为数据持久化网关的纯粹角色。
|
||||
|
||||
- **职责**:负责 `Plan` 及其直接子对象(`Task`, `SubPlan`)的 CRUD 操作。
|
||||
- **实现**:
|
||||
- `CreatePlan` 和 `UpdatePlanMetadataAndStructure` 方法将被简化。它们不再需要任何特殊的关联处理逻辑(如 `Association().Replace()`)。
|
||||
- 只需接收一个由 `PlanService` 准备好的、`task.Devices` 字段已被填充的 `plan` 对象。
|
||||
- 在 `CreatePlan` 中,调用 `tx.Create(plan)` 时,GORM 会自动级联创建 `Plan`、`Task` 以及 `device_tasks` 中的关联记录。
|
||||
- 在 `UpdatePlanMetadataAndStructure` 的 `reconcileTasks` 逻辑中,对于新创建的任务,GORM 的 `tx.Create(task)` 同样会自动处理其设备关联。
|
||||
|
||||
### 3.5. 整体流程
|
||||
|
||||
以 **创建计划** 为例:
|
||||
|
||||
1. `PlanController` 调用 `PlanService.CreatePlan(plan)`。
|
||||
2. `PlanService` 调用 `unitOfWork.ExecuteInTransaction` 启动一个数据库事务。
|
||||
3. 在事务闭包内,`PlanService` 遍历 `plan` 对象中的所有 `task`。
|
||||
4. 对于每一个 `task` 模型,调用 `taskFactory.CreateTaskFromModel(task)` 创建一个临时的领域对象。
|
||||
5. 调用该领域对象的 `ResolveDeviceIDs()` 方法,获取其使用的设备 ID 列表。
|
||||
6. 如果返回了设备 ID 列表,则使用事务性的 `DeviceRepository` 查询出 `[]models.Device` 实体。
|
||||
7. 所有 `task` 的关联数据准备好后,调用事务性的 `PlanRepository.CreatePlan(plan)`。GORM 在创建 `plan` 和 `task` 的同时,会自动创建
|
||||
`device_tasks` 表中的关联记录。
|
||||
8. `UnitOfWork` 提交事务。
|
||||
|
||||
**更新计划** 的流程与创建类似,在 `UpdatePlanMetadataAndStructure` 方法中,由于会先删除旧任务再创建新任务,因此在创建新任务后执行相同的设备关联步骤。
|
||||
|
||||
**删除计划** 时,由于 `Task` 模型上配置了 `OnDelete:CASCADE`,GORM 会自动删除关联的 `Task` 记录。同时,GORM 的多对多删除逻辑会自动清理
|
||||
`device_tasks` 表中与被删除任务相关的记录。因此 `DeletePlan` 方法无需修改。
|
||||
|
||||
## 4. 实施步骤
|
||||
|
||||
1. **扩展 `TaskFactory` 接口**
|
||||
- 在 `internal/domain/plan/task.go` 文件中,为 `TaskFactory` 接口添加
|
||||
`CreateTaskFromModel(*models.Task) (TaskDeviceIDResolver, error)` 方法。
|
||||
|
||||
2. **实现 `TaskFactory` 新方法**
|
||||
- 在 `internal/domain/task/task.go` 文件中,为 `taskFactory` 结构体实现 `CreateTaskFromModel` 方法。
|
||||
|
||||
3. **修改 `PlanService`**
|
||||
- 在 `internal/domain/plan/plan_service.go` 中:
|
||||
- 修改 `planServiceImpl` 结构体,增加 `unitOfWork repository.UnitOfWork` 和 `taskFactory TaskFactory` 字段。
|
||||
- 修改 `NewPlanService` 构造函数,接收并注入这些新依赖。
|
||||
- 重构 `CreatePlan` 和 `UpdatePlan` 方法,使用 `UnitOfWork` 包裹事务,并在其中实现数据准备和关联逻辑。
|
||||
|
||||
4. **修改 `PlanRepository`**
|
||||
- 在 `internal/infra/repository/plan_repository.go` 中:
|
||||
- **简化 `CreatePlan` 和 `UpdatePlanMetadataAndStructure` 方法**。移除所有手动处理设备关联的代码(例如,如果之前有 `Association("Devices").Replace()` 等调用,则应删除)。
|
||||
- 确保这两个方法的核心逻辑就是调用 GORM 的 `Create` 或 `Updates`,信任 GORM 会根据传入模型中已填充的 `Devices` 字段来自动维护多对多关联。
|
||||
|
||||
5. **修改依赖注入**
|
||||
- 在 `internal/core/component_initializers.go` (或类似的依赖注入入口文件) 中:
|
||||
- 将 `unitOfWork` 和 `taskFactory` 实例传递给 `plan.NewPlanService` 的构造函数。
|
||||
|
||||
## 5. 结论
|
||||
|
||||
此方案通过复用现有的领域对象和工厂模式,优雅地解决了设备关联维护的问题。它保持了清晰的架构分层和模块职责,在实现功能的同时,为项目未来的扩展和维护奠定了坚实、可扩展的基础。
|
||||
@@ -0,0 +1,103 @@
|
||||
# 设备与任务多对多关联模型设计
|
||||
|
||||
## 需求背景
|
||||
|
||||
用户需要为系统中的“设备”和“任务”增加多对多关联,即一个设备可以执行多个任务,一个任务可以被多个设备执行。
|
||||
|
||||
## 现有模型分析
|
||||
|
||||
### `internal/infra/models/device.go`
|
||||
|
||||
`Device` 模型定义:
|
||||
|
||||
```go
|
||||
type Device struct {
|
||||
gorm.Model
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
DeviceTemplateID uint `gorm:"not null;index" json:"device_template_id"`
|
||||
DeviceTemplate DeviceTemplate `json:"device_template"`
|
||||
AreaControllerID uint `gorm:"not null;index" json:"area_controller_id"`
|
||||
AreaController AreaController `json:"area_controller"`
|
||||
Location string `gorm:"index" json:"location"`
|
||||
Properties datatypes.JSON `json:"properties"`
|
||||
}
|
||||
```
|
||||
|
||||
### `internal/infra/models/plan.go`
|
||||
|
||||
`Task` 模型定义:
|
||||
|
||||
```go
|
||||
type Task struct {
|
||||
ID int `gorm:"primarykey"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||
|
||||
PlanID uint `gorm:"not null;index" json:"plan_id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Description string `json:"description"`
|
||||
ExecutionOrder int `gorm:"not null" json:"execution_order"`
|
||||
Type TaskType `gorm:"not null" json:"type"`
|
||||
Parameters datatypes.JSON `json:"parameters"`
|
||||
}
|
||||
```
|
||||
|
||||
## 方案设计
|
||||
|
||||
为了实现设备和任务的多对多关系,我们将引入一个中间关联模型 `DeviceTask`。考虑到 `Task` 模型定义在 `plan.go` 中,为了保持相关模型的内聚性,我们将 `DeviceTask` 模型也定义在 `internal/infra/models/plan.go` 文件中。
|
||||
|
||||
### 1. 在 `internal/infra/models/plan.go` 中新增 `DeviceTask` 关联模型
|
||||
|
||||
`DeviceTask` 模型将包含 `DeviceID` 和 `TaskID` 作为外键,以及 GORM 的标准模型字段。
|
||||
|
||||
```go
|
||||
// DeviceTask 是设备和任务之间的关联模型,表示一个设备可以执行多个任务,一个任务可以被多个设备执行。
|
||||
type DeviceTask struct {
|
||||
gorm.Model
|
||||
DeviceID uint `gorm:"not null;index"` // 设备ID
|
||||
TaskID uint `gorm:"not null;index"` // 任务ID
|
||||
|
||||
// 可选:如果需要存储关联的额外信息,可以在这里添加字段,例如:
|
||||
// Configuration datatypes.JSON `json:"configuration"` // 任务在特定设备上的配置
|
||||
}
|
||||
|
||||
// TableName 自定义 GORM 使用的数据库表名
|
||||
func (DeviceTask) TableName() string {
|
||||
return "device_tasks"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 修改 `internal/infra/models/device.go`
|
||||
|
||||
在 `Device` 结构体中添加 `Tasks` 字段,通过 `gorm:"many2many:device_tasks;"` 标签声明与 `Task` 的多对多关系,并指定中间表名为 `device_tasks`。
|
||||
|
||||
```go
|
||||
// Device 代表系统中的所有普通设备
|
||||
type Device struct {
|
||||
gorm.Model
|
||||
|
||||
// ... 其他现有字段 ...
|
||||
|
||||
// Tasks 是与此设备关联的任务列表,通过 DeviceTask 关联表实现多对多关系
|
||||
Tasks []Task `gorm:"many2many:device_tasks;" json:"tasks"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 修改 `internal/infra/models/plan.go`
|
||||
|
||||
在 `Task` 结构体中添加 `Devices` 字段,通过 `gorm:"many2many:device_tasks;"` 标签声明与 `Device` 的多对多关系,并指定中间表名为 `device_tasks`。
|
||||
|
||||
```go
|
||||
// Task 代表计划中的一个任务,具有执行顺序
|
||||
type Task struct {
|
||||
// ... 其他现有字段 ...
|
||||
|
||||
// Devices 是与此任务关联的设备列表,通过 DeviceTask 关联表实现多对多关系
|
||||
Devices []Device `gorm:"many2many:device_tasks;" json:"devices"`
|
||||
}
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
通过上述修改,我们将在数据库中创建一个名为 `device_tasks` 的中间表,用于存储 `Device` 和 `Task` 之间的关联关系。在 Go 代码层面,`Device` 和 `Task` 模型将能够直接通过 `Tasks` 和 `Devices` 字段进行多对多关系的查询和操作。
|
||||
@@ -0,0 +1,24 @@
|
||||
# 需求
|
||||
|
||||
删除设备/设备模板/区域主控前进行校验
|
||||
|
||||
## issue
|
||||
|
||||
http://git.huangwc.com/pig/pig-farm-controller/issues/50
|
||||
|
||||
## 需求描述
|
||||
|
||||
1. 删除设备时检测是否被任务使用
|
||||
2. 删除设备模板时检测是否被设备使用
|
||||
3. 删除区域主控时检测是否被设备使用
|
||||
|
||||
# 实现
|
||||
|
||||
1. [重构计划领域](./plan_service_refactor.md)
|
||||
2. [让任务可以提供自身使用设备](./add_get_device_id_configs_to_task.md)
|
||||
3. [现有计划管理逻辑迁移](./plan_service_refactor_to_domain.md)
|
||||
4. [增加设备任务关联表](./device_task_many_to_many_design.md)
|
||||
5. [增加任务增删改查时对设备任务关联表的维护](./device_task_association_maintenance.md)
|
||||
6. [删除设备时检查](./check_before_device_deletion.md)
|
||||
7. [删除设备模板时检查和删除区域主控时检查](./refactor_deletion_check.md)
|
||||
8. [优化设备服务方法的入参](./refactor_id_conversion.md)
|
||||
@@ -0,0 +1,83 @@
|
||||
# 计划服务重构设计方案
|
||||
|
||||
## 1. 目标
|
||||
|
||||
将 `internal/domain/scheduler` 包重构为 `internal/domain/plan`,并创建一个新的 `Service` 对象,将原 `scheduler`
|
||||
包中的核心调度逻辑集成到 `Service` 中作为一个子服务,统一由 `Service`
|
||||
对外提供服务。此重构旨在提高代码的模块化、可维护性和可测试性,并为后续的“设备删除前校验”功能奠定基础。
|
||||
|
||||
## 2. 方案详情
|
||||
|
||||
### 2.1. 包重命名
|
||||
|
||||
* 将 `internal/domain/scheduler` 目录重命名为 `internal/domain/plan`。
|
||||
* 修改 `internal/domain/plan` 目录下所有 Go 文件中的 `package scheduler` 为 `package plan`。
|
||||
* 更新 `internal/domain/plan` 目录下所有 Go 文件中所有引用
|
||||
`git.huangwc.com/pig/pig-farm-controller/internal/domain/scheduler` 的导入路径为
|
||||
`git.huangwc.com/pig/pig-farm-controller/internal/domain/plan`。
|
||||
|
||||
### 2.2. `internal/domain/plan` 包内部结构调整
|
||||
|
||||
* **`internal/domain/plan/task.go`**:
|
||||
* 保持不变。它定义了任务的接口和工厂,是领域内的核心抽象。
|
||||
|
||||
* **`internal/domain/plan/plan_execution_manager.go`**:
|
||||
* 将 `Scheduler` 结构体更名为 `ExecutionManagerImpl`。这个名称更准确地反映了它作为计划任务执行的协调者和管理者的具体实现。
|
||||
* 将 `NewScheduler` 构造函数更名为 `NewExecutionManagerImpl`。
|
||||
* 文件内部所有对 `Scheduler` 的引用都将更新为 `ExecutionManagerImpl`。
|
||||
|
||||
* **`internal/domain/plan/analysis_plan_task_manager.go`**:
|
||||
* 将 `AnalysisPlanTaskManager` 结构体更名为 `AnalysisPlanTaskManagerImpl`。
|
||||
* 将 `NewAnalysisPlanTaskManager` 构造函数更名为 `NewAnalysisPlanTaskManagerImpl`。
|
||||
* 文件内部所有对 `AnalysisPlanTaskManager` 的引用都将更新为 `AnalysisPlanTaskManagerImpl`。
|
||||
|
||||
* **定义领域层接口**:
|
||||
* 在 `internal/domain/plan` 包中定义 `ExecutionManager` 接口,包含 `ExecutionManagerImpl` 对外暴露的所有公共方法。
|
||||
* 在 `internal/domain/plan` 包中定义 `AnalysisPlanTaskManager` 接口,包含 `AnalysisPlanTaskManagerImpl` 对外暴露的所有公共方法。
|
||||
* `ExecutionManagerImpl` 和 `AnalysisPlanTaskManagerImpl` 将分别实现对应的接口。
|
||||
|
||||
### 2.3. 创建 `internal/domain/plan/plan_service.go`
|
||||
|
||||
* 创建新文件 `internal/domain/plan/plan_service.go`。
|
||||
|
||||
* **定义领域服务接口**:
|
||||
* 在 `internal/domain/plan` 包中定义 `Service` 接口,该接口将聚合 `ExecutionManager` 和 `AnalysisPlanTaskManager`
|
||||
的所有公共方法,并由 `planServiceImpl` 实现这些方法的委托。
|
||||
|
||||
* **实现领域服务**:
|
||||
* 该文件将定义 `planServiceImpl` 结构体,并包含 `ExecutionManager` 接口和 `AnalysisPlanTaskManager` 接口的实例作为其依赖。
|
||||
* 实现 `NewService` 构造函数,负责接收 `ExecutionManager` 接口和 `AnalysisPlanTaskManager` 接口的实例,并将其注入到
|
||||
`planServiceImpl` 中。
|
||||
* `planServiceImpl` 将对外提供高层次的 API,这些 API 会协调调用其依赖的接口方法。例如:
|
||||
* `Service.Start()` 方法会调用 `ExecutionManager` 接口的 `Start()` 方法。
|
||||
* `Service.Stop()` 方法会调用 `ExecutionManager` 接口的 `Stop()` 方法。
|
||||
* `Service.RefreshPlanTriggers()` 方法会调用 `AnalysisPlanTaskManager` 接口的 `Refresh()` 方法。
|
||||
* `Service.CreateOrUpdateTrigger()` 方法会调用 `AnalysisPlanTaskManager` 接口的 `CreateOrUpdateTrigger()` 方法。
|
||||
* `Service.EnsureAnalysisTaskDefinition()` 方法会调用 `AnalysisPlanTaskManager` 接口的
|
||||
`EnsureAnalysisTaskDefinition()` 方法。
|
||||
* 未来所有与计划相关的领域操作,都将通过 `Service` 接口进行。
|
||||
|
||||
### 2.4. 调整依赖注入和引用
|
||||
|
||||
* **查找并替换导入路径:** 使用 `grep` 命令查找整个项目中所有引用
|
||||
`git.huangwc.com/pig/pig-farm-controller/internal/domain/scheduler` 的地方,并将其替换为
|
||||
`git.huangwc.com/pig/pig-farm-controller/internal/domain/plan`。
|
||||
* **更新 `internal/core/component_initializers.go`**:
|
||||
* 在初始化阶段,我们将创建 `plan.ExecutionManagerImpl` 和 `plan.AnalysisPlanTaskManagerImpl` 的具体实例。
|
||||
* 然后,将这些具体实例作为 `plan.ExecutionManager` 接口和 `plan.AnalysisPlanTaskManager` 接口类型传递给
|
||||
`plan.NewService` 构造函数,创建 `planServiceImpl` 实例。
|
||||
* 最终,`plan.NewService` 返回 `plan.Service` 接口类型。
|
||||
* 应用程序的其他部分将通过 `plan.Service` 接口来访问计划相关的逻辑,而不是直接访问底层的管理器或其具体实现。
|
||||
|
||||
## 3. 优势
|
||||
|
||||
* **职责分离清晰:** `internal/domain/plan` 包专注于计划领域的核心逻辑和管理,并提供统一的 `Service` 接口作为领域服务的入口。
|
||||
* **符合领域驱动设计:** 领域层包含核心业务逻辑和管理器,应用层(如果需要)作为领域层的协调者。
|
||||
* **与现有项目风格一致:** 借鉴 `domain/pig` 包的模式,提高了项目内部的一致性。
|
||||
* **可测试性增强:** `Service` 可以更容易地进行单元测试,因为其依赖的接口可以被模拟。
|
||||
* **可维护性提高:** 当计划相关的业务逻辑发生变化时,可以更精确地定位到需要修改的组件。
|
||||
* **松耦合:** `Service` 不依赖于具体的实现,而是依赖于接口,提高了系统的灵活性和可扩展性。
|
||||
|
||||
## 4. 验证和测试
|
||||
|
||||
在完成所有修改后,需要运行项目并进行测试,确保调度器功能正常,没有引入新的错误。
|
||||
@@ -0,0 +1,179 @@
|
||||
# 重构方案:将 `app/service/plan_service.go` 的核心逻辑迁移到 `domain/plan/plan_service.go`
|
||||
|
||||
## 目标:
|
||||
|
||||
* `app/service/plan_service.go` (应用服务层): 仅负责接收 DTO、将 DTO 转换为领域实体、调用 `domain/plan/plan_service` 的领域方法,并将领域方法返回的领域实体转换为 DTO 返回。
|
||||
* `domain/plan/plan_service.go` (领域层): 封装所有与计划相关的业务逻辑、验证规则、状态管理以及对领域实体的查询操作。
|
||||
|
||||
## 详细步骤:
|
||||
|
||||
### 第一步:修改 `domain/plan/plan_service.go` (领域层)
|
||||
|
||||
1. **引入必要的依赖**:
|
||||
* `repository.PlanRepository`:用于与计划数据存储交互。
|
||||
* `repository.DeviceRepository`:如果计划逻辑中需要设备信息。
|
||||
* `models.Plan`:领域实体。
|
||||
* `errors` 和 `gorm.ErrRecordNotFound`:用于错误处理。
|
||||
* `models.PlanTypeSystem`, `models.PlanStatusEnabled`, `models.PlanContentTypeSubPlans`, `models.PlanContentTypeTasks` 等常量。
|
||||
* `git.huangwc.com/pig/pig-farm-controller/internal/infra/models`
|
||||
* `git.huangwc.com/pig/pig-farm-controller/internal/infra/repository`
|
||||
* `errors`
|
||||
* `gorm.io/gorm`
|
||||
|
||||
2. **定义领域层错误**: 将 `app/service/plan_service.go` 中定义的错误(`ErrPlanNotFound`, `ErrPlanCannotBeModified` 等)迁移到 `domain/plan/plan_service.go`,并根据领域层的语义进行调整。
|
||||
|
||||
```go
|
||||
var (
|
||||
// ErrPlanNotFound 表示未找到计划
|
||||
ErrPlanNotFound = errors.New("计划不存在")
|
||||
// ErrPlanCannotBeModified 表示计划不允许修改
|
||||
ErrPlanCannotBeModified = errors.New("系统计划不允许修改")
|
||||
// ErrPlanCannotBeDeleted 表示计划不允许删除
|
||||
ErrPlanCannotBeDeleted = errors.New("系统计划不允许删除")
|
||||
// ErrPlanCannotBeStarted 表示计划不允许手动启动
|
||||
ErrPlanCannotBeStarted = errors.New("系统计划不允许手动启动")
|
||||
// ErrPlanAlreadyEnabled 表示计划已处于启动状态
|
||||
ErrPlanAlreadyEnabled = errors.New("计划已处于启动状态,无需重复操作")
|
||||
// ErrPlanNotEnabled 表示计划未处于启动状态
|
||||
ErrPlanNotEnabled = errors.New("计划当前不是启用状态")
|
||||
// ErrPlanCannotBeStopped 表示计划不允许停止
|
||||
ErrPlanCannotBeStopped = errors.New("系统计划不允许停止")
|
||||
)
|
||||
```
|
||||
|
||||
3. **扩展 `plan.Service` 接口**:
|
||||
* 将 `app/service/plan_service.go` 中 `PlanService` 接口的所有方法(`CreatePlan`, `GetPlanByID`, `ListPlans`, `UpdatePlan`, `DeletePlan`, `StartPlan`, `StopPlan`)添加到 `domain/plan/Service` 接口中。
|
||||
* 这些方法的参数和返回值将直接使用领域实体(`*models.Plan`)或基本类型,而不是 DTO。例如:
|
||||
* `CreatePlan(plan *models.Plan) (*models.Plan, error)`
|
||||
* `GetPlanByID(id uint) (*models.Plan, error)`
|
||||
* `ListPlans(opts repository.ListPlansOptions, page, pageSize int) ([]models.Plan, int64, error)`
|
||||
* `UpdatePlan(plan *models.Plan) (*models.Plan, error)`
|
||||
* `DeletePlan(id uint) error`
|
||||
* `StartPlan(id uint) error`
|
||||
* `StopPlan(id uint) error`
|
||||
|
||||
4. **修改 `planServiceImpl` 结构体**:
|
||||
* 添加 `planRepo repository.PlanRepository` 字段。
|
||||
* 添加 `deviceRepo repository.DeviceRepository` 字段 (如果需要)。
|
||||
* `analysisPlanTaskManager plan.AnalysisPlanTaskManager` 字段保持不变。
|
||||
|
||||
```go
|
||||
type planServiceImpl struct {
|
||||
executionManager ExecutionManager
|
||||
taskManager AnalysisPlanTaskManager
|
||||
planRepo repository.PlanRepository // 新增
|
||||
// deviceRepo repository.DeviceRepository // 如果需要,新增
|
||||
logger *logs.Logger
|
||||
}
|
||||
```
|
||||
|
||||
5. **实现 `plan.Service` 接口中的新方法**:
|
||||
* 将 `app/service/plan_service.go` 中 `planService` 的所有业务逻辑方法(`CreatePlan`, `GetPlanByID`, `ListPlans`, `UpdatePlan`, `DeletePlan`, `StartPlan`, `StopPlan`)的实现,迁移到 `domain/plan/planServiceImpl` 中。
|
||||
* **关键修改点**:
|
||||
* **参数和返回值**: 确保这些方法现在接收和返回的是 `*models.Plan` 或其他领域实体,而不是 DTO。
|
||||
* **业务逻辑**: 保留所有的业务规则、验证和状态管理逻辑。
|
||||
* **依赖**: 这些方法将直接调用 `planRepo` 和 `analysisPlanTaskManager`。
|
||||
* **日志**: 日志记录保持不变,但可能需要调整日志信息以反映领域层的上下文。
|
||||
* **错误处理**: 错误处理逻辑保持不变,但现在将返回领域层定义的错误。
|
||||
* **ContentType 自动判断**: `CreatePlan` 和 `UpdatePlan` 中的 `ContentType` 自动判断逻辑应该保留在领域层。
|
||||
* **执行计数器重置**: `UpdatePlan` 和 `StartPlan` 中的执行计数器重置逻辑应该保留在领域层。
|
||||
* **系统计划限制**: 对系统计划的修改、删除、启动、停止限制逻辑应该保留在领域层。
|
||||
* **验证和重排顺序**: `models.Plan` 的 `ValidateExecutionOrder()` 和 `ReorderSteps()` 方法的调用应该在 `CreatePlan` 和 `UpdatePlan` 方法的领域层实现中进行,而不是在 DTO 转换函数中。
|
||||
|
||||
6. **修改 `NewPlanService` 函数**: 接收 `repository.PlanRepository` 和 `repository.DeviceRepository` (如果需要) 作为参数,并注入到 `planServiceImpl` 中。
|
||||
|
||||
```go
|
||||
func NewPlanService(
|
||||
executionManager ExecutionManager,
|
||||
taskManager AnalysisPlanTaskManager,
|
||||
planRepo repository.PlanRepository, // 新增
|
||||
// deviceRepo repository.DeviceRepository, // 如果需要,新增
|
||||
logger *logs.Logger,
|
||||
) Service {
|
||||
return &planServiceImpl{
|
||||
executionManager: executionManager,
|
||||
taskManager: taskManager,
|
||||
planRepo: planRepo, // 注入
|
||||
// deviceRepo: deviceRepo, // 注入
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第二步:修改 `app/service/plan_service.go` (应用服务层)
|
||||
|
||||
1. **修改 `planService` 结构体**:
|
||||
* 移除 `planRepo repository.PlanRepository` 字段。
|
||||
* 将 `analysisPlanTaskManager plan.AnalysisPlanTaskManager` 字段替换为 `domainPlanService plan.Service`。
|
||||
|
||||
```go
|
||||
type planService struct {
|
||||
logger *logs.Logger
|
||||
// planRepo repository.PlanRepository // 移除
|
||||
domainPlanService plan.Service // 替换为领域层的服务接口
|
||||
// analysisPlanTaskManager plan.AnalysisPlanTaskManager // 移除,由 domainPlanService 内部持有
|
||||
}
|
||||
```
|
||||
|
||||
2. **修改 `NewPlanService` 函数**:
|
||||
* 接收 `domainPlanService plan.Service` 作为参数。
|
||||
* 将 `planRepo` 和 `analysisPlanTaskManager` 的注入替换为 `domainPlanService`。
|
||||
|
||||
```go
|
||||
func NewPlanService(
|
||||
logger *logs.Logger,
|
||||
// planRepo repository.PlanRepository, // 移除
|
||||
domainPlanService plan.Service, // 接收领域层服务
|
||||
// analysisPlanTaskManager plan.AnalysisPlanTaskManager, // 移除
|
||||
) PlanService {
|
||||
return &planService{
|
||||
logger: logger,
|
||||
domainPlanService: domainPlanService, // 注入领域层服务
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **修改 `PlanService` 接口**:
|
||||
* 接口定义保持不变,仍然接收和返回 DTO。
|
||||
|
||||
4. **修改 `planService` 接口实现**:
|
||||
* **`CreatePlan`**:
|
||||
* 接收 `dto.CreatePlanRequest`。
|
||||
* 使用 `dto.NewPlanFromCreateRequest` 将 DTO 转换为 `*models.Plan`。**注意:此时 `NewPlanFromCreateRequest` 不再包含 `ValidateExecutionOrder()` 和 `ReorderSteps()` 的调用。**
|
||||
* 调用 `s.domainPlanService.CreatePlan(*models.Plan)`。
|
||||
* 将返回的 `*models.Plan` 转换为 `dto.PlanResponse`。
|
||||
* **`GetPlanByID`**:
|
||||
* 调用 `s.domainPlanService.GetPlanByID(id)`。
|
||||
* 将返回的 `*models.Plan` 转换为 `dto.PlanResponse`。
|
||||
* **`ListPlans`**:
|
||||
* 将 `dto.ListPlansQuery` 转换为 `repository.ListPlansOptions`。
|
||||
* 调用 `s.domainPlanService.ListPlans(...)`。
|
||||
* 将返回的 `[]models.Plan` 转换为 `[]dto.PlanResponse`。
|
||||
* **`UpdatePlan`**:
|
||||
* 使用 `dto.NewPlanFromUpdateRequest` 将 `dto.UpdatePlanRequest` 转换为 `*models.Plan`。**注意:此时 `NewPlanFromUpdateRequest` 不再包含 `ValidateExecutionOrder()` 和 `ReorderSteps()` 的调用。**
|
||||
* 设置 `plan.ID = id`。
|
||||
* 调用 `s.domainPlanService.UpdatePlan(*models.Plan)`。
|
||||
* 将返回的 `*models.Plan` 转换为 `dto.PlanResponse`。
|
||||
* **`DeletePlan`**:
|
||||
* 调用 `s.domainPlanService.DeletePlan(id)`。
|
||||
* **`StartPlan`**:
|
||||
* 调用 `s.domainPlanService.StartPlan(id)`。
|
||||
* **`StopPlan`**:
|
||||
* 调用 `s.domainPlanService.StopPlan(id)`。
|
||||
* **错误处理**: 应用服务层将捕获领域层返回的错误,并可能将其转换为更适合应用服务层或表示层的错误信息(例如,将领域层的 `ErrPlanNotFound` 转换为 `app/service` 层定义的 `ErrPlanNotFound`)。
|
||||
|
||||
### 第三步:修改 `internal/app/dto/plan_converter.go`
|
||||
|
||||
1. **移除 `NewPlanFromCreateRequest` 和 `NewPlanFromUpdateRequest` 中的领域逻辑**:
|
||||
* 从 `NewPlanFromCreateRequest` 和 `NewPlanFromUpdateRequest` 函数中移除 `plan.ValidateExecutionOrder()` 和 `plan.ReorderSteps()` 的调用。这些逻辑应该由领域服务来处理。
|
||||
|
||||
### 第四步:更新 `main.go` 或其他依赖注入点
|
||||
|
||||
* 调整 `NewPlanService` 的调用,确保正确注入 `domain/plan/Service` 的实现。
|
||||
|
||||
## 风险与注意事项:
|
||||
|
||||
* **事务管理**: 如果领域层的方法需要事务,确保事务在领域层内部或由应用服务层协调。
|
||||
* **错误映射**: 仔细处理领域层错误到应用服务层错误的映射,确保对外暴露的错误信息是恰当的。
|
||||
* **循环依赖**: 确保 `domain` 层不依赖 `app` 层,`app` 层可以依赖 `domain` 层。
|
||||
* **测试**: 重构后需要对所有相关功能进行全面的单元测试和集成测试。
|
||||
@@ -0,0 +1,196 @@
|
||||
# 重构方案:将删除前关联检查逻辑迁移至 Service 层
|
||||
|
||||
## 1. 目标
|
||||
|
||||
将删除**区域主控 (AreaController)** 和**设备模板 (DeviceTemplate)** 时的关联设备检查逻辑,从 Repository(数据访问)层重构至 Service(业务逻辑)层。
|
||||
|
||||
## 2. 动机
|
||||
|
||||
当前实现中,关联检查逻辑位于 Repository 层的 `Delete` 方法内。这违反了分层架构的最佳实践。Repository 层应仅负责单纯的数据持久化操作(增删改查),而不应包含业务规则。
|
||||
|
||||
通过本次重构,我们将实现:
|
||||
- **职责分离**: Service 层负责编排业务逻辑(如“删除前必须检查关联”),Repository 层回归其数据访问的单一职责。
|
||||
- **代码清晰**: 业务流程在 Service 层一目了然,便于理解和维护。
|
||||
- **可测试性增强**: 可以独立测试 Service 层的业务规则,而无需依赖数据库的事务或约束。
|
||||
|
||||
## 3. 详细实施步骤
|
||||
|
||||
### 第 1 步:在 Service 层定义业务错误
|
||||
|
||||
在 `internal/app/service/device_service.go` 文件中,导出两个新的错误变量,用于清晰地表达业务约束。
|
||||
|
||||
```go
|
||||
// ErrAreaControllerInUse 表示区域主控正在被设备使用,无法删除
|
||||
var ErrAreaControllerInUse = errors.New("区域主控正在被一个或多个设备使用,无法删除")
|
||||
|
||||
// ErrDeviceTemplateInUse 表示设备模板正在被设备使用,无法删除
|
||||
var ErrDeviceTemplateInUse = errors.New("设备模板正在被一个或多个设备使用,无法删除")
|
||||
```
|
||||
|
||||
### 第 2 步:调整 Repository 接口与实现
|
||||
|
||||
#### 2.1 `device_repository.go`
|
||||
在 `DeviceRepository` 接口中增加一个方法,用于检查区域主控是否被使用,并在 `gormDeviceRepository` 中实现它。
|
||||
|
||||
```go
|
||||
// internal/infra/repository/device_repository.go
|
||||
|
||||
type DeviceRepository interface {
|
||||
// ... 其他方法
|
||||
IsAreaControllerInUse(areaControllerID uint) (bool, error)
|
||||
}
|
||||
|
||||
func (r *gormDeviceRepository) IsAreaControllerInUse(areaControllerID uint) (bool, error) {
|
||||
var count int64
|
||||
if err := r.db.Model(&models.Device{}).Where("area_controller_id = ?", areaControllerID).Count(&count).Error; err != nil {
|
||||
return false, fmt.Errorf("检查区域主控使用情况失败: %w", err)
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 `area_controller_repository.go`
|
||||
简化 `Delete` 方法,移除所有业务逻辑,使其成为一个纯粹的数据库删除操作。
|
||||
|
||||
```go
|
||||
// internal/infra/repository/area_controller_repository.go
|
||||
|
||||
func (r *gormAreaControllerRepository) Delete(id uint) error {
|
||||
// 移除原有的事务和关联检查
|
||||
if err := r.db.Delete(&models.AreaController{}, id).Error; err != nil {
|
||||
return fmt.Errorf("删除区域主控失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 `device_template_repository.go`
|
||||
同样,简化 `Delete` 方法。`IsInUse` 方法保持不变,因为它仍然是一个有用的查询。
|
||||
|
||||
```go
|
||||
// internal/infra/repository/device_template_repository.go
|
||||
|
||||
func (r *gormDeviceTemplateRepository) Delete(id uint) error {
|
||||
// 移除原有的关联检查逻辑
|
||||
if err := r.db.Delete(&models.DeviceTemplate{}, id).Error; err != nil {
|
||||
return fmt.Errorf("删除设备模板失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### 第 3 步:在 Service 层实现业务逻辑
|
||||
|
||||
#### 3.1 `device_service.go` - 删除区域主控
|
||||
修改 `DeleteAreaController` 方法,加入关联检查的业务逻辑。
|
||||
|
||||
```go
|
||||
// internal/app/service/device_service.go
|
||||
|
||||
func (s *deviceService) DeleteAreaController(id string) error {
|
||||
idUint, err := strconv.ParseUint(id, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无效的ID格式: %w", err)
|
||||
}
|
||||
acID := uint(idUint)
|
||||
|
||||
// 1. 检查是否存在
|
||||
_, err = s.areaControllerRepo.FindByID(acID)
|
||||
if err != nil {
|
||||
return err // 如果未找到,gorm会返回 ErrRecordNotFound
|
||||
}
|
||||
|
||||
// 2. 检查是否被使用(业务逻辑)
|
||||
inUse, err := s.deviceRepo.IsAreaControllerInUse(acID)
|
||||
if err != nil {
|
||||
return err // 返回数据库检查错误
|
||||
}
|
||||
if inUse {
|
||||
return ErrAreaControllerInUse // 返回业务错误
|
||||
}
|
||||
|
||||
// 3. 执行删除
|
||||
return s.areaControllerRepo.Delete(acID)
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 `device_service.go` - 删除设备模板
|
||||
修改 `DeleteDeviceTemplate` 方法,加入关联检查的业务逻辑。
|
||||
|
||||
```go
|
||||
// internal/app/service/device_service.go
|
||||
|
||||
func (s *deviceService) DeleteDeviceTemplate(id string) error {
|
||||
idUint, err := strconv.ParseUint(id, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无效的ID格式: %w", err)
|
||||
}
|
||||
dtID := uint(idUint)
|
||||
|
||||
// 1. 检查是否存在
|
||||
_, err = s.deviceTemplateRepo.FindByID(dtID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 检查是否被使用(业务逻辑)
|
||||
inUse, err := s.deviceTemplateRepo.IsInUse(dtID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if inUse {
|
||||
return ErrDeviceTemplateInUse // 返回业务错误
|
||||
}
|
||||
|
||||
// 3. 执行删除
|
||||
return s.deviceTemplateRepo.Delete(dtID)
|
||||
}
|
||||
```
|
||||
|
||||
### 第 4 步:在 Controller 层处理新的业务错误
|
||||
|
||||
#### 4.1 `device_controller.go` - 删除区域主控
|
||||
在 `DeleteAreaController` 的错误处理中,增加对 `ErrAreaControllerInUse` 的捕获,并返回 `409 Conflict` 状态码。
|
||||
|
||||
```go
|
||||
// internal/app/controller/device/device_controller.go
|
||||
|
||||
func (c *Controller) DeleteAreaController(ctx echo.Context) error {
|
||||
// ...
|
||||
if err := c.deviceService.DeleteAreaController(acID); err != nil {
|
||||
switch {
|
||||
case errors.Is(err, gorm.ErrRecordNotFound):
|
||||
// ...
|
||||
case errors.Is(err, service.ErrAreaControllerInUse): // 新增
|
||||
c.logger.Warnf("%s: 尝试删除正在被使用的主控, ID: %s", actionType, acID)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "主控正在被使用", acID)
|
||||
default:
|
||||
// ...
|
||||
}
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 `device_controller.go` - 删除设备模板
|
||||
在 `DeleteDeviceTemplate` 的错误处理中,增加对 `ErrDeviceTemplateInUse` 的捕获,并返回 `409 Conflict` 状态码。
|
||||
|
||||
```go
|
||||
// internal/app/controller/device/device_controller.go
|
||||
|
||||
func (c *Controller) DeleteDeviceTemplate(ctx echo.Context) error {
|
||||
// ...
|
||||
if err := c.deviceService.DeleteDeviceTemplate(dtID); err != nil {
|
||||
switch {
|
||||
case errors.Is(err, gorm.ErrRecordNotFound):
|
||||
// ...
|
||||
case errors.Is(err, service.ErrDeviceTemplateInUse): // 新增
|
||||
c.logger.Warnf("%s: 尝试删除正在被使用的模板, ID: %s", actionType, dtID)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeConflict, err.Error(), actionType, "模板正在被使用", dtID)
|
||||
default:
|
||||
// ...
|
||||
}
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,106 @@
|
||||
# 重构方案:将 ID 类型转换逻辑迁移至 Controller 层
|
||||
|
||||
## 1. 目标
|
||||
|
||||
将所有通过 URL 路径传入的 `id`(`string` 类型),其到 `uint` 类型的转换和验证逻辑,从 Service(业务逻辑)层统一迁移至 Controller(控制器)层。
|
||||
|
||||
## 2. 动机
|
||||
|
||||
当前实现中,Controller 将从 URL 获取的 `string` 类型的 ID 直接传递给 Service 层,由 Service 层负责使用 `strconv.ParseUint` 进行类型转换。
|
||||
|
||||
这种模式存在以下问题:
|
||||
- **职责不清**:Service 层被迫处理了本应属于输入验证和转换的逻辑,而这部分工作更贴近 Controller 的职责。
|
||||
- **Service 不纯粹**:业务核心逻辑与原始输入(字符串)耦合,降低了 Service 的可复用性。理想情况下,Service 的接口应该只处理内部定义的、类型安全的数据。
|
||||
- **延迟的错误处理**:对于一个无效的 ID(如 "abc"),请求会穿透到 Service 层才会失败,而这种格式错误在 Controller 层就应该被拦截。
|
||||
|
||||
通过本次重构,我们将实现:
|
||||
- **职责分离**:Controller 负责处理 HTTP 请求的原始数据(验证、转换),Service 负责处理核心业务。
|
||||
- **接口清晰**:Service 层的所有方法将只接受类型安全的 `uint` 作为 ID,使其意图更加明确。
|
||||
- **快速失败**:无效的 ID 将在 Controller 层被立即拒绝,并返回 `400 Bad Request`,提高了系统的健壮性。
|
||||
|
||||
## 3. 详细实施步骤
|
||||
|
||||
### 第 1 步:修改 `device_service.go`
|
||||
|
||||
#### 3.1 修改 `DeviceService` 接口
|
||||
所有接收 `id string` 参数的方法签名,全部修改为接收 `id uint`。
|
||||
|
||||
**受影响的方法列表:**
|
||||
- `GetDevice(id string)` -> `GetDevice(id uint)`
|
||||
- `UpdateDevice(id string, ...)` -> `UpdateDevice(id uint, ...)`
|
||||
- `DeleteDevice(id string)` -> `DeleteDevice(id uint)`
|
||||
- `ManualControl(id string, ...)` -> `ManualControl(id uint, ...)`
|
||||
- `GetAreaController(id string)` -> `GetAreaController(id uint)`
|
||||
- `UpdateAreaController(id string, ...)` -> `UpdateAreaController(id uint, ...)`
|
||||
- `DeleteAreaController(id string)` -> `DeleteAreaController(id uint)`
|
||||
- `GetDeviceTemplate(id string)` -> `GetDeviceTemplate(id uint)`
|
||||
- `UpdateDeviceTemplate(id string, ...)` -> `UpdateDeviceTemplate(id uint, ...)`
|
||||
- `DeleteDeviceTemplate(id string)` -> `DeleteDeviceTemplate(id uint)`
|
||||
|
||||
#### 3.2 修改 `deviceService` 实现
|
||||
在 `deviceService` 的方法实现中,移除所有 `strconv.ParseUint` 的调用,直接使用传入的 `uint` 类型的 ID。
|
||||
|
||||
**示例 (`DeleteDeviceTemplate`):**
|
||||
|
||||
**修改前:**
|
||||
```go
|
||||
func (s *deviceService) DeleteDeviceTemplate(id string) error {
|
||||
idUint, err := strconv.ParseUint(id, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无效的ID格式: %w", err)
|
||||
}
|
||||
dtID := uint(idUint)
|
||||
// ... 业务逻辑
|
||||
}
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```go
|
||||
func (s *deviceService) DeleteDeviceTemplate(id uint) error {
|
||||
// 直接使用 id
|
||||
// ... 业务逻辑
|
||||
}
|
||||
```
|
||||
|
||||
### 第 2 步:修改 `device_controller.go`
|
||||
|
||||
在所有调用受影响 Service 方法的 Controller 方法中,增加 ID 的转换和错误处理逻辑。
|
||||
|
||||
**示例 (`DeleteDeviceTemplate`):**
|
||||
|
||||
**修改前:**
|
||||
```go
|
||||
func (c *Controller) DeleteDeviceTemplate(ctx echo.Context) error {
|
||||
const actionType = "删除设备模板"
|
||||
dtID := ctx.Param("id") // dtID is a string
|
||||
|
||||
if err := c.deviceService.DeleteDeviceTemplate(dtID); err != nil {
|
||||
// ... 错误处理
|
||||
}
|
||||
// ... 成功处理
|
||||
}
|
||||
```
|
||||
|
||||
**修改后:**
|
||||
```go
|
||||
func (c *Controller) DeleteDeviceTemplate(ctx echo.Context) error {
|
||||
const actionType = "删除设备模板"
|
||||
idStr := ctx.Param("id")
|
||||
|
||||
// 在 Controller 层进行转换和验证
|
||||
idUint, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.logger.Warnf("%s: 无效的ID格式: %s", actionType, idStr)
|
||||
return controller.SendErrorWithAudit(ctx, controller.CodeBadRequest, "无效的ID格式", actionType, "ID格式错误", idStr)
|
||||
}
|
||||
dtID := uint(idUint)
|
||||
|
||||
// 调用 Service,传入 uint 类型的 ID
|
||||
if err := c.deviceService.DeleteDeviceTemplate(dtID); err != nil {
|
||||
// ... 错误处理 (保持不变)
|
||||
}
|
||||
|
||||
// ... 成功处理
|
||||
}
|
||||
```
|
||||
此模式将应用于所有受影响的 Controller 方法。
|
||||
@@ -0,0 +1,89 @@
|
||||
# 实现方案:纯 Context 驱动的调用链追踪
|
||||
|
||||
本方案旨在提供一个绝对安全、符合 Go 语言习惯且对业务代码侵入性最小的调用链追踪方案。其核心思想是:**调用链信息完全由标准的 `context.Context` 承载,并通过 `logs` 包提供的一系列无状态包级函数进行原子化、安全的操作。**
|
||||
|
||||
### 1. 核心设计原则
|
||||
|
||||
- **`Context` 是唯一载体**: 所有的调用链信息,包括组件名(对象名)和函数名(方法名),都只存储在标准的 `context.Context` 中。我们不再定义任何自定义的 `Context` 接口,以保证最大的兼容性。
|
||||
|
||||
- **纯粹的包级函数**: `logs` 包将提供一系列纯粹的、无状态的包级函数,作为与 `Context` 交互的唯一 API。这些函数负责向 `Context` 中添加信息,或从 `Context` 中生成 `Logger`。
|
||||
|
||||
- **无状态的 `Logger`**: `Logger` 对象本身不再携带任何调用链信息。它在被 `logs.GetLogger(ctx)` 生成时,才被一次性地赋予包含完整调用链的配置。
|
||||
|
||||
### 2. 实现细节
|
||||
|
||||
#### a. Context 中存储的数据
|
||||
|
||||
`context.Context` 将通过 `context.WithValue` 在幕后存储两种核心信息,这两种信息都使用 `logs` 包内部的私有 `key` 类型,以避免与其他包的键冲突。
|
||||
|
||||
- **组件名 (`compNameKey`)**: 用于存储一个字符串,表示当前上下文环境属于哪个组件(例如 `\"组件1\"`)。
|
||||
- **调用链 (`chainKey`)**: 用于存储一个字符串切片 (`[]string`),记录了从请求开始到当前位置的完整调用路径(例如 `[\"组件2.Create\", \"组件1.Create\"]`)。
|
||||
|
||||
#### b. `logs` 包提供的核心 API
|
||||
|
||||
`logs` 包需要对外提供以下四个核心的包级函数,以提供不同粒度的灵活性:
|
||||
|
||||
1. **`logs.AddCompName(ctx, compName) context.Context`**
|
||||
- **职责**: 将一个组件名(对象名)存入 `Context`,并返回一个包含该信息的新 `Context`。这通常在依赖注入时完成,用于创建组件的“身份名牌” `selfCtx`。
|
||||
- **实现细节**: 该函数接收一个 `context.Context` 和一个 `compName` 字符串。它内部使用 `context.WithValue`,以私有的 `compNameKey` 为键,将 `compName` 字符串存入 `Context`,然后返回这个全新的 `Context`。
|
||||
|
||||
2. **`logs.AddFuncName(upstreamCtx, selfCtx, funcName) context.Context`**
|
||||
- **职责**: 这是构建调用链的核心原子操作。它智能地合并上游的调用链和当前组件的信息,生成并返回一个包含更新后调用链和当前组件名的 **新 `Context`**。此函数用于只需要传递调用链而不需要立即打印日志的场景。
|
||||
- **实现细节**:
|
||||
1. 函数接收 `upstreamCtx`, `selfCtx`, `funcName`。
|
||||
2. **获取上游调用链**: 从 `upstreamCtx` 中,通过 `chainKey` 读取出已经存在的调用链(`oldChain []string`)。
|
||||
3. **获取当前组件名**: 从 `selfCtx` 中,通过 `compNameKey` 读取出当前组件的名称(`compName string`)。
|
||||
4. **构建新节点**: 将 `compName` 和 `funcName` 拼接成一个新节点(例如 `\"组件2.Create\"`)。
|
||||
5. **生成新调用链**: 将这个新节点追加到 `oldChain` 的末尾,形成 `newChain []string`。
|
||||
6. **创建新的 Context**:
|
||||
- 使用 `context.WithValue`,以 `chainKey` 为键,将 `newChain` 存入一个新的 `Context`,我们称之为 `tmpCtx`。
|
||||
- 接着,**(关键修正)** 基于 `tmpCtx`,再次调用 `context.WithValue`,以 `compNameKey` 为键,将从 `selfCtx` 中获取的 `compName` 存入,得到最终的 `newCtx`。这确保了传向下游的 `Context` 正确地标识了当前组件。
|
||||
7. **返回**: 返回 `newCtx`。
|
||||
|
||||
3. **`logs.GetLogger(ctx) *Logger`**
|
||||
- **职责**: 从 `Context` 中生成最终的、可用于打印的 `Logger` 实例。
|
||||
- **实现细节**:
|
||||
1. 函数接收一个 `context.Context`。
|
||||
2. 它从 `ctx` 中,通过 `chainKey` 读取出完整的调用链 `[]string`。
|
||||
3. 如果调用链不存在或为空,它就生成一个不带 `trace` 字段的普通 `Logger` 实例并返回。
|
||||
4. 如果调用链存在,它就用 `->` 符号将切片中的所有节点拼接成一个完整的 `trace` 字符串。
|
||||
5. 最后,它创建一个**一次性**的 `Logger` 实例,将这个 `trace` 字符串和底层的 `zap` 配置传给它,然后返回这个准备就绪的 `Logger`。
|
||||
|
||||
4. **`logs.Trace(upstreamCtx, selfCtx, funcName) (context.Context, *Logger)`**
|
||||
- **职责**: 作为 `AddFuncName` 和 `GetLogger` 的便捷封装,一步到位地完成调用链构建和 `Logger` 生成。用于需要立即打印日志的场景。
|
||||
- **实现细节**:
|
||||
1. 内部调用 `newCtx := logs.AddFuncName(upstreamCtx, selfCtx, funcName)`。
|
||||
2. 内部调用 `logger := logs.GetLogger(newCtx)`。
|
||||
3. 返回 `newCtx` 和 `logger`。
|
||||
|
||||
### 3. 最终使用模式
|
||||
|
||||
#### a. 依赖注入阶段
|
||||
|
||||
(保持不变)在应用启动和组装依赖时,我们不再注入 `Logger` 对象,而是为每个组件创建一个包含其自身名称的专属 `Context`,并将这个 `Context` 注入到组件实例中。
|
||||
|
||||
- **流程**:
|
||||
1. 在依赖注入的根源处,创建一个全局的、初始的 `context.Background()`。
|
||||
2. 对于需要被追踪的组件(例如 `组件1`),调用 `logs.AddCompName(ctx, \"组件1\")` 来创建一个 `ctxForC1`。
|
||||
3. 在创建 `组件1` 的实例时,将这个 `ctxForC1` 作为其成员变量(例如 `selfCtx`)保存起来。这个 `selfCtx` 就成了 `组件1` 的“身份名牌”。
|
||||
|
||||
#### b. 请求处理阶段
|
||||
|
||||
开发者可以根据需求灵活选择 API。
|
||||
|
||||
- **场景一:需要立即打印日志 (推荐)**:
|
||||
1. 在方法入口处,立即调用 `ctx, logger := logs.Trace(upstreamCtx, z.selfCtx, \"Create\")`。
|
||||
2. 使用这个 `logger` 打印日志:`logger.Info(\"创建组件2\")`。
|
||||
3. 当需要调用下游方法时,将返回的 **新 `Context` (`ctx`)** 传递下去。
|
||||
|
||||
- **场景二:只需要传递调用链**:
|
||||
1. 在方法入口处,调用 `ctx := logs.AddFuncName(upstreamCtx, z.selfCtx, \"Create\")`。
|
||||
2. 这个方法本身不打印日志,但在调用下游方法时,将这个包含了更新后调用链的 **新 `Context` (`ctx`)** 传递下去。
|
||||
|
||||
- **场景三:在方法中间打印日志**:
|
||||
1. 一个方法可能在执行了一部分逻辑后才需要打印日志。
|
||||
2. `ctx := logs.AddFuncName(upstreamCtx, z.selfCtx, \"Create\")` // 先在入口更新调用链
|
||||
3. // ... 执行一些业务逻辑 ...
|
||||
4. `logger := logs.GetLogger(ctx)` // 在需要时,基于更新后的 ctx 获取 logger
|
||||
5. `logger.Info(\"业务逻辑执行到一半\")`
|
||||
6. // ... 继续执行并传递 ctx ...
|
||||
@@ -0,0 +1,21 @@
|
||||
# 需求
|
||||
|
||||
为日志系统提供一种机制,能够记录和追踪跨越不同对象和方法的调用链,以增强日志的可读性和可观测性。
|
||||
|
||||
## issue
|
||||
|
||||
http://git.huangwc.com/pig/pig-farm-controller/issues/56
|
||||
|
||||
## 描述
|
||||
|
||||
### 初始问题
|
||||
在复杂的业务流程中,一个请求可能会流经多个服务或控制器。为了追踪一个完整的操作流程,开发者不得不在每个方法的日志中手动添加上下文信息(如 `actionType`),这非常繁琐且容易出错。
|
||||
|
||||
### 期望的演进
|
||||
1. **初步设想**: 提供一个 `logger.With()` 方法,调用后返回一个携带预设信息的日志记录器副本,后续日志自动附带此信息。
|
||||
2. **解决上下文覆盖**: 简单的 `With("action", "some_action")` 会导致同名字段被覆盖。因此,期望能将调用链信息存储在数组中,并在打印时拼接,如 `action: "action1·action2"`。
|
||||
3. **最终目标 (可观测性)**: 进一步区分“对象”和“方法”,构建出更具表现力的调用链。例如,当 `userController` 的 `Create` 方法调用 `userService` 的 `Create` 方法时,日志应能清晰地展示 `trace: "userController.Create->userService.Create"` 这样的调用关系。
|
||||
|
||||
## 实现方案
|
||||
|
||||
[实现方案](./implementation.md)
|
||||
@@ -0,0 +1,9 @@
|
||||
- **`internal/app/api/api.go` (`API`)**:
|
||||
- [x] 修改 `NewAPI` 函数,移除 `logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 移除 `API` 结构体中的 `logger` 成员,改为保存 `Ctx context.Context`。
|
||||
- [x] 为 `API` 组件本身创建 `Ctx`:`Ctx := logs.AddCompName(ctx, 'API')`,并传递给所有 `Controller`
|
||||
的构造函数。
|
||||
- [x] 改造 `Start` 方法,从 `a.Ctx` 获取 `logger` 实例进行日志记录。
|
||||
- [x] 改造 `Stop` 方法,从 `a.Ctx` 获取 `logger` 实例进行日志记录。
|
||||
- **`internal/app/api/router.go`
|
||||
- [x] 改造 `setupRoutes` 方法,从 `a.Ctx` 获取 `logger` 实例进行日志记录。
|
||||
@@ -0,0 +1,113 @@
|
||||
- **`internal/app/controller/user/user_controller.go` (`user.Controller`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `Controller` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `Ctx context.Context` 成员。
|
||||
- **构造函数改造 (`NewController`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 将这个 `Ctx` 赋值给 `Controller` 结构体的 `Ctx` 成员。
|
||||
- **公共方法改造 (`CreateUser`, `Login`, `SendTestNotification`)**:
|
||||
- [x] 在每个方法入口处,从 `echo.Context` 中提取 `request.Context()` 作为 `upstreamCtx`。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(upstreamCtx, c.Ctx, "MethodName")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有对 `c.logger.Errorf` 和 `c.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `c.userService` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/app/controller/device/device_controller.go` (`device.Controller`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `Controller` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `Ctx context.Context` 成员。
|
||||
- **构造函数改造 (`NewController`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 将这个 `Ctx` 赋值给 `Controller` 结构体的 `Ctx` 成员。
|
||||
- **公共方法改造 (`CreateDevice`, `GetDevice`, `ListDevices`, `UpdateDevice`, `DeleteDevice`, `ManualControl`,
|
||||
`CreateAreaController`, `GetAreaController`, `ListAreaControllers`, `UpdateAreaController`,
|
||||
`DeleteAreaController`, `CreateDeviceTemplate`, `GetDeviceTemplate`, `ListDeviceTemplates`,
|
||||
`UpdateDeviceTemplate`, `DeleteDeviceTemplate`)**:
|
||||
- [x] 在每个方法入口处,从 `echo.Context` 中提取 `request.Context()` 作为 `upstreamCtx`。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(upstreamCtx, c.Ctx, "MethodName")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有对 `c.logger.Errorf`, `c.logger.Warnf`, `c.logger.Infof` 的调用替换为 `logger.Errorf`,
|
||||
`logger.Warnf`, `logger.Infof`。
|
||||
- [x] 确保所有对 `c.deviceService` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/app/controller/plan/plan_controller.go` (`plan.Controller`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `Controller` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `Ctx context.Context` 成员。
|
||||
- **构造函数改造 (`NewController`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 将这个 `Ctx` 赋值给 `Controller` 结构体的 `Ctx` 成员。
|
||||
- **公共方法改造 (`CreatePlan`, `GetPlan`, `ListPlans`, `UpdatePlan`, `DeletePlan`, `StartPlan`, `StopPlan`)**:
|
||||
- [x] 在每个方法入口处,从 `echo.Context` 中提取 `request.Context()` 作为 `upstreamCtx`。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(upstreamCtx, c.Ctx, "MethodName")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有对 `c.logger.Errorf` 和 `c.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `c.planService` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/app/controller/management/pig_farm_controller.go` (`management.PigFarmController`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `PigFarmController` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `Ctx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPigFarmController`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 将这个 `Ctx` 赋值给 `PigFarmController` 结构体的 `Ctx` 成员。
|
||||
- **公共方法改造 (`CreatePigHouse`, `GetPigHouse`, `ListPigHouses`, `UpdatePigHouse`, `DeletePigHouse`, `CreatePen`,
|
||||
`GetPen`, `ListPens`, `UpdatePen`, `DeletePen`, `UpdatePenStatus`)**:
|
||||
- [x] 在每个方法入口处,从 `echo.Context` 中提取 `request.Context()` 作为 `upstreamCtx`。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(upstreamCtx, c.Ctx, "MethodName")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有对 `c.logger.Errorf` 和 `c.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `c.service` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/app/controller/management/pig_batch_controller.go` (`management.PigBatchController`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `PigBatchController` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `Ctx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPigBatchController`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 将这个 `Ctx` 赋值给 `PigBatchController` 结构体的 `Ctx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `CreatePigBatch`, `GetPigBatch`, `UpdatePigBatch`, `DeletePigBatch`, `ListPigBatches`,
|
||||
`AssignEmptyPensToBatch`, `ReclassifyPenToNewBatch`, `RemoveEmptyPenFromBatch`, `MovePigsIntoPen`)**:
|
||||
- [x] 这些方法通过调用 `controller_helpers.go` 中的 `handleAPIRequest...` 系列函数来处理请求。
|
||||
- [x] 确保传递给 `handleAPIRequest...` 系列函数的 `serviceExecutor` 匿名函数,其签名与 `controller_helpers.go`
|
||||
改造后期望的签名一致(即接收 `context.Context` 作为第一个参数)。
|
||||
- [x] 在 `serviceExecutor` 匿名函数内部,将接收到的 `context.Context` 传递给 `c.service` 的相应方法。
|
||||
- **`internal/app/controller/management/controller_helpers.go`**
|
||||
- [x] 修改 `mapAndSendError` 函数中的 `c.logger.Errorf` 调用,改为从 `echo.Context` 中提取 `context.Context`,并使用
|
||||
`logs.Trace` 或 `logs.GetLogger` 获取 `logger` 实例进行日志记录。
|
||||
- [x] 检查其他函数是否直接或间接依赖 `*PigBatchController` 的 `logger` 成员,并进行相应改造。
|
||||
- **`internal/app/controller/management/pig_batch_trade_controller.go` (`management.PigBatchController`)**
|
||||
- [x] 修改 `NewController` 函数,移除 `logger` 参数,改为接收 `Ctx context.Context`。
|
||||
- [x] 移除 `Controller` 结构体中的 `logger` 成员,改为保存 `Ctx`。
|
||||
- [x] **所有公共方法**: 接收 `echo.Context` 的方法,需要从中提取 `request.Context()` 作为 `upstreamCtx`。
|
||||
- **`internal/app/controller/management/pig_batch_health_controller.go` (`management.PigBatchController`)**
|
||||
- [x] 修改 `NewController` 函数,移除 `logger` 参数,改为接收 `Ctx context.Context`。
|
||||
- [x] 移除 `Controller` 结构体中的 `logger` 成员,改为保存 `Ctx`。
|
||||
- [x] **所有公共方法**: 接收 `echo.Context` 的方法,需要从中提取 `request.Context()` 作为 `upstreamCtx`。
|
||||
- **`internal/app/controller/management/pig_batch_transfer_controller.go` (`management.PigBatchController`)**
|
||||
- [x] 修改 `NewController` 函数,移除 `logger` 参数,改为接收 `Ctx context.Context`。
|
||||
- [x] 移除 `Controller` 结构体中的 `logger` 成员,改为保存 `Ctx`。
|
||||
- [x] **所有公共方法**: 接收 `echo.Context` 的方法,需要从中提取 `request.Context()` 作为 `upstreamCtx`。
|
||||
- **`internal/app/controller/monitor/monitor_controller.go` (`monitor.Controller`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `Controller` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `Ctx context.Context` 成员。
|
||||
- **构造函数改造 (`NewController`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 将这个 `Ctx` 赋值给 `Controller` 结构体的 `Ctx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `ListSensorData`, `ListDeviceCommandLogs`, `ListPlanExecutionLogs`, `ListTaskExecutionLogs`,
|
||||
`ListPendingCollections`, `ListUserActionLogs`, `ListRawMaterialPurchases`, `ListRawMaterialStockLogs`,
|
||||
`ListFeedUsageRecords`, `ListMedicationLogs`, `ListPigBatchLogs`, `ListWeighingBatches`, `ListWeighingRecords`,
|
||||
`ListPigTransferLogs`, `ListPigSickLogs`, `ListPigPurchases`, `ListPigSales`, `ListNotifications`)**:
|
||||
- [x] 在每个方法入口处,从 `echo.Context` 中提取 `request.Context()` 作为 `upstreamCtx`。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(upstreamCtx, c.Ctx, actionType)` 获取新的 `context.Context` 和
|
||||
`logger` 实例(`actionType` 为方法内部定义的常量)。
|
||||
- [x] 将所有对 `c.logger.Errorf`, `c.logger.Warnf`, `c.logger.Infof` 的调用替换为 `logger.Errorf`,
|
||||
`logger.Warnf`, `logger.Infof`。
|
||||
- [x] 确保所有对 `c.monitorService` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
|
||||
|
||||
- **`internal/app/controller/response.go`**
|
||||
- [x] 修改 `SendSuccessWithAudit` 和 `SendErrorWithAudit` 函数签名,使其接收 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在 `setAuditDetails` 函数中,如果需要,从传入的 `context.Context` 中提取调用链信息,并将其添加到审计信息中。
|
||||
- **`internal/app/controller/auth_utils.go`**
|
||||
- [x] 检查 `GetOperatorIDFromContext` 和 `GetOperatorFromContext` 函数,确保它们能够正确地从 `echo.Context` 中获取
|
||||
`request.Context()`,并从中提取用户信息。
|
||||
@@ -0,0 +1,272 @@
|
||||
- **`internal/domain/audit/service.go` (`audit.Service`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `service` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `audit.Service` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "AuditService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `service` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`LogAction`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "LogAction")` 获取新的 `context.Context`
|
||||
和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Warnw` 和 `s.logger.Errorw` 的调用替换为 `logger.Warnw` 和 `logger.Errorw`。
|
||||
- [x] 确保所有对 `s.userActionLogRepository.Create` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/device/general_device_service.go` (`device.Service`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `GeneralDeviceService` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGeneralDeviceService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `GeneralDeviceService` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "GeneralDeviceService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `GeneralDeviceService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Switch`, `Collect`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, g.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `g.logger.Errorf`, `g.logger.Infof`, `g.logger.Warnf`, `g.logger.Debugf`, `g.logger.DPanicf`
|
||||
的调用替换为 `logger.Errorf`, `logger.Infof`, `logger.Warnf`, `logger.Debugf`, `logger.DPanicf`。
|
||||
- [x] 确保所有对 `g.deviceRepo`, `g.deviceCommandLogRepo`, `g.pendingCollectionRepo`, `g.comm` 等依赖的调用都将
|
||||
`newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/notify/notify.go` (`domain_notify.Service` - `failoverService` 实现)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `failoverService` 结构体中的 `log *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewFailoverService`)**:
|
||||
- [x] 修改函数签名,移除 `log *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `failoverService` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "FailoverService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `failoverService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`SendBatchAlarm`, `BroadcastAlarm`, `SendTestMessage`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.log.Infow`, `s.log.Errorw`, `s.log.Warnw` 的调用替换为 `logger.Infow`, `logger.Errorw`,
|
||||
`logger.Warnw`。
|
||||
- [x] 确保所有对 `s.userRepo`, `s.primaryNotifier.Send`, `s.notifiers`, `s.notificationRepo` 等依赖的调用都将
|
||||
`newCtx` 作为第一个参数传递。
|
||||
- **内部辅助方法改造 (`sendAlarmToUser`, `recordNotificationAttempt`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.log.Errorw`, `s.log.Infow`, `s.log.Warnw` 的调用替换为 `logger.Errorw`, `logger.Infow`,
|
||||
`logger.Warnw`。
|
||||
- [x] 确保所有对 `s.userRepo`, `s.primaryNotifier.Send`, `s.notifiers`, `s.notificationRepo` 等依赖的调用都将
|
||||
`newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/pig/pen_transfer_manager.go` (`pig.PigPenTransferManager`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `pigPenTransferManager` 结构体中可能存在的 `logger *logs.Logger` 成员(如果未来添加)。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPigPenTransferManager`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `pigPenTransferManager` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigPenTransferManager")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `pigPenTransferManager` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `LogTransfer`, `GetPenByID`, `GetPensByBatchID`, `UpdatePenFields`, `GetCurrentPigsInPen`,
|
||||
`GetTotalPigsInPensForBatchTx`, `ReleasePen`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数(在 `tx *gorm.DB` 之前)。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf` 和 `s.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `s.penRepo`, `s.logRepo`, `s.pigBatchRepo` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/pig/pig_trade_manager.go` (`pig.PigTradeManager`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPigTradeManager`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `pigTradeManager` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigTradeManager")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `pigTradeManager` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`SellPig`, `BuyPig`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数(在 `tx *gorm.DB` 之前)。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 确保所有对 `s.tradeRepo` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/pig/pig_sick_manager.go` (`pig.SickPigManager`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewSickPigManager`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `sickPigManager` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "SickPigManager")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `sickPigManager` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`ProcessSickPigLog`, `GetCurrentSickPigCount`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数(在 `tx *gorm.DB` 之前)。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 确保所有对 `s.sickLogRepo`, `s.medicationLogRepo` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/pig/pig_batch_service.go` (`pig.PigBatchService`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPigBatchService`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `pigBatchService` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigBatchService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `pigBatchService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `CreatePigBatch`, `GetPigBatch`, `UpdatePigBatch`, `DeletePigBatch`, `ListPigBatches`,
|
||||
`AssignEmptyPensToBatch`, `MovePigsIntoPen`, `ReclassifyPenToNewBatch`, `RemoveEmptyPenFromBatch`,
|
||||
`GetCurrentPigQuantity`, `GetCurrentPigsInPen`, `GetTotalPigsInPensForBatch`, `UpdatePigBatchQuantity`,
|
||||
`SellPigs`, `BuyPigs`, `TransferPigsAcrossBatches`, `TransferPigsWithinBatch`, `RecordSickPigs`,
|
||||
`RecordSickPigRecovery`, `RecordSickPigDeath`, `RecordSickPigCull`, `RecordDeath`, `RecordCull`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 确保所有对 `s.pigBatchRepo`, `s.pigBatchLogRepo`, `s.uow`, `s.transferSvc`, `s.tradeSvc`, `s.sickSvc`
|
||||
等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/plan/analysis_plan_task_manager.go` (`plan.AnalysisPlanTaskManager`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `analysisPlanTaskManagerImpl` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewAnalysisPlanTaskManager`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `analysisPlanTaskManagerImpl` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "AnalysisPlanTaskManager")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `analysisPlanTaskManagerImpl` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Refresh`, `CreateOrUpdateTrigger`, `EnsureAnalysisTaskDefinition`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, m.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `m.logger.Info`, `m.logger.Errorf`, `m.logger.Warnf` 的调用替换为 `logger.Info`,
|
||||
`logger.Errorf`, `logger.Warnf`。
|
||||
- [x] 确保所有对 `m.planRepo`, `m.pendingTaskRepo`, `m.executionLogRepo` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **内部辅助方法改造 (`getRefreshData`, `cleanupInvalidTasks`, `addOrUpdateTriggers`, `createTriggerTask`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, m.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `m.logger.Info`, `m.logger.Errorf`, `m.logger.Warnf` 的调用替换为 `logger.Info`,
|
||||
`logger.Errorf`, `logger.Warnf`。
|
||||
- [x] 确保所有对 `m.planRepo`, `m.pendingTaskRepo`, `m.executionLogRepo` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/plan/plan_execution_manager.go` (`plan.ExecutionManager`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `planExecutionManagerImpl` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPlanExecutionManager`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `planExecutionManagerImpl` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PlanExecutionManager")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `planExecutionManagerImpl` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Start`, `Stop`)**:
|
||||
- [x] 在 `Start` 方法中,使用 `newCtx, logger := logs.Trace(context.Background(), s.selfCtx, "Start")` 获取
|
||||
`logger` 实例进行日志记录。
|
||||
- [x] 在 `Stop` 方法中,使用 `newCtx, logger := logs.Trace(context.Background(), s.selfCtx, "Stop")` 获取
|
||||
`logger` 实例进行日志记录。
|
||||
- **内部辅助方法改造 (`run`, `claimAndSubmit`, `handleRequeue`, `processTask`, `runTask`, `analysisPlan`,
|
||||
`updateTaskExecutionLogStatus`, `handlePlanTermination`, `handlePlanCompletion`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数(如果方法内部需要传递上下文)。
|
||||
- [x] 在每个方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf`, `s.logger.Warnf`, `s.logger.Infof`, `s.logger.DPanicf` 的调用替换为
|
||||
`logger.Errorf`, `logger.Warnf`, `logger.Infof`, `logger.DPanicf`。
|
||||
- [x] 确保所有对 `s.pendingTaskRepo`, `s.executionLogRepo`, `s.deviceRepo`, `s.sensorDataRepo`, `s.planRepo`,
|
||||
`s.analysisPlanTaskManager`, `s.taskFactory`, `s.deviceService` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/plan/plan_service.go` (`plan.Service`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `planServiceImpl` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPlanService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `planServiceImpl` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PlanService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `planServiceImpl` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Start`, `Stop`, `RefreshPlanTriggers`, `CreatePlan`, `GetPlanByID`, `ListPlans`, `UpdatePlan`,
|
||||
`DeletePlan`, `StartPlan`, `StopPlan`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf`, `s.logger.Infof`, `s.logger.Warnf` 的调用替换为 `logger.Errorf`,
|
||||
`logger.Infof`, `logger.Warnf`。
|
||||
- [x] 确保所有对 `s.executionManager`, `s.taskManager`, `s.planRepo`, `s.deviceRepo`, `s.unitOfWork`,
|
||||
`s.taskFactory` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/task/task.go` (`task.TaskFactory`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `taskFactory` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewTaskFactory`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `taskFactory` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "TaskFactory")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `taskFactory` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Production`, `CreateTaskFromModel`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法内部,使用 `newCtx, logger := logs.Trace(ctx, t.selfCtx, "MethodName")` 获取新的 `context.Context`
|
||||
和 `logger` 实例。
|
||||
- [x] 将所有对 `t.logger.Panicf` 的调用替换为 `logger.Panicf`。
|
||||
- [x] 将所有对 `NewDelayTask`, `NewReleaseFeedWeightTask`, `NewFullCollectionTask` 的调用,传递 `newCtx`。
|
||||
- **`internal/domain/task/release_feed_weight_task.go`**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `ReleaseFeedWeightTask` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewReleaseFeedWeightTask`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `ReleaseFeedWeightTask` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "ReleaseFeedWeightTask")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `ReleaseFeedWeightTask` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Execute`, `OnFailure`, `ResolveDeviceIDs`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, r.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `r.logger.Infof`, `r.logger.Errorf`, `r.logger.Warnf`, `r.logger.Debugf` 的调用替换为
|
||||
`logger.Infof`, `logger.Errorf`, `logger.Warnf`, `logger.Debugf`。
|
||||
- [x] 确保所有对 `r.deviceRepo`, `r.sensorDataRepo`, `r.feedPort` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **内部辅助方法改造 (`getNowWeight`, `parseParameters`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, r.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `r.logger.Errorf`, `r.logger.Warnf` 的调用替换为 `logger.Errorf`, `logger.Warnf`。
|
||||
- [x] 确保所有对 `r.sensorDataRepo`, `r.deviceRepo` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/task/full_collection_task.go`**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `FullCollectionTask` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewFullCollectionTask`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `FullCollectionTask` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "FullCollectionTask")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `FullCollectionTask` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Execute`, `OnFailure`, `ResolveDeviceIDs`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, t.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `t.logger.Infow`, `t.logger.Errorw` 的调用替换为 `logger.Infow`, `logger.Errorw`。
|
||||
- [x] 确保所有对 `t.deviceRepo`, `t.deviceService` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/domain/task/delay_task.go`**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `DelayTask` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewDelayTask`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `DelayTask` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "DelayTask")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `DelayTask` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Execute`, `OnFailure`, `ResolveDeviceIDs`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, d.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `d.logger.Infof` 和 `d.logger.Errorf` 的调用替换为 `logger.Infof` 和 `logger.Errorf`。
|
||||
- **内部辅助方法改造 (`parseParameters`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, d.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `d.logger.Errorf` 的调用替换为 `logger.Errorf`。
|
||||
- **`internal/domain/token/token_service.go` (`token.Service`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewTokenService`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `tokenService` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "TokenService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `tokenService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`GenerateToken`, `ParseToken`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.*` 的调用替换为 `logger.*` (如果存在)。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
- **`internal/infra/database/storage.go` (`database.Storage`)**
|
||||
- **工厂函数改造 (`NewStorage`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,将接收到的 `ctx` 作为第一个参数传递给 `NewPostgresStorage` 函数。
|
||||
- **`internal/infra/database/postgres.go`**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `PostgresStorage` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPostgresStorage`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `PostgresStorage` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PostgresStorage")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `PostgresStorage` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Connect`, `Disconnect`, `Migrate`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, ps.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `ps.logger.Info`, `ps.logger.Errorw`, `ps.logger.Debugw` 的调用替换为 `logger.Info`,
|
||||
`logger.Errorw`, `logger.Debugw`。
|
||||
- [x] 在 `Connect` 方法中,调用 `logs.NewGormLogger` 时,将 `newCtx` 传递给它。
|
||||
- [x] 在 `Connect` 方法中,调用 `gorm.Open` 时,使用
|
||||
`ps.db, err = gorm.Open(postgres.Open(ps.connectionString), &gorm.Config{Logger: logs.NewGormLogger(newCtx)})`。
|
||||
- [x] 在 `Migrate` 方法中,确保所有对 `ps.db.AutoMigrate` 和 `ps.db.Exec` 的调用都使用 `newCtx`。
|
||||
- **内部辅助方法改造 (`setupTimescaleDB`, `creatingHyperTable`, `applyCompressionPolicies`, `creatingIndex`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在每个方法入口处,使用 `newCtx, logger := logs.Trace(ctx, ps.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `ps.logger.Info`, `ps.logger.Errorw`, `ps.logger.Debugw`, `ps.logger.Warnw`, `ps.logger.Debug`
|
||||
的调用替换为 `logger.Info`, `logger.Errorw`, `logger.Debugw`, `logger.Warnw`, `logger.Debug`。
|
||||
- [x] 确保所有对 `ps.db.Exec` 的调用都使用 `newCtx`。
|
||||
|
||||
- **`internal/infra/notify/log_notifier.go` (`notify.LogNotifier`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `logNotifier` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewLogNotifier`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `logNotifier` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "LogNotifier")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `logNotifier` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Send`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, l.selfCtx, "Send")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有对 `l.logger.Infow` 的调用替换为 `logger.Infow`。
|
||||
- **公共方法 (`Type`)**:
|
||||
- [x] 此方法不涉及日志或上下文传递,无需改造。
|
||||
- **`internal/infra/notify/lark.go` (`notify.LarkNotifier`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewLarkNotifier`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `larkNotifier` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "LarkNotifier")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `larkNotifier` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Send`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, l.selfCtx, "Send")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有内部的错误日志(例如 `fmt.Errorf` 返回的错误,以及 `getAccessToken` 中的错误)通过 `logger.Errorf`
|
||||
记录。
|
||||
- **内部辅助方法改造 (`getAccessToken`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, l.selfCtx, "getAccessToken")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有内部的错误日志通过 `logger.Errorf` 记录。
|
||||
- **`internal/infra/notify/smtp.go` (`notify.SMTPNotifier`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewSMTPNotifier`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `smtpNotifier` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "SMTPNotifier")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `smtpNotifier` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Send`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "Send")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有内部的错误日志(例如 `fmt.Errorf` 返回的错误)通过 `logger.Errorf` 记录。
|
||||
- **`internal/infra/notify/wechat.go` (`notify.WechatNotifier`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewWechatNotifier`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `wechatNotifier` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "WechatNotifier")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `wechatNotifier` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Send`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, w.selfCtx, "Send")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有内部的错误日志(例如 `fmt.Errorf` 返回的错误,以及 `getAccessToken` 中的错误)通过 `logger.Errorf`
|
||||
记录。
|
||||
- **内部辅助方法改造 (`getAccessToken`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, w.selfCtx, "getAccessToken")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有内部的错误日志通过 `logger.Errorf` 记录。
|
||||
|
||||
- **`internal/infra/transport/lora/chirp_stack.go` (`lora.ChirpStackTransport`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `ChirpStackTransport` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewChirpStackTransport`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `ChirpStackTransport` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "ChirpStackTransport")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `ChirpStackTransport` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Send`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, c.selfCtx, "Send")` 获取新的 `context.Context` 和
|
||||
`logger` 实例。
|
||||
- [x] 将所有对 `c.logger.Errorf` 和 `c.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 在调用 `c.client.DeviceService.DeviceServiceEnqueue` 时,确保将 `newCtx` 传递给
|
||||
`params.WithContext(newCtx)`,以便 ChirpStack 客户端内部的 HTTP 请求也能携带上下文。
|
||||
- **`internal/infra/transport/lora/lora_mesh_uart_passthrough_transport.go` (`lora.LoRaMeshUartPassthroughTransport`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `LoRaMeshUartPassthroughTransport` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewLoRaMeshUartPassthroughTransport`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `LoRaMeshUartPassthroughTransport` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "LoRaMeshUartPassthroughTransport")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `LoRaMeshUartPassthroughTransport` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Listen`, `Send`, `Stop`)**:
|
||||
- [x] 修改 `Listen` 方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 修改 `Send` 方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 修改 `Stop` 方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, t.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `t.logger.Info`, `t.logger.Errorf` 的调用替换为 `logger.Info`, `logger.Errorf`。
|
||||
- [x] 在 `Send` 方法中,确保 `t.sendChan <- req` 传递的 `req` 包含了 `newCtx`。
|
||||
- **内部辅助方法改造 (`workerLoop`, `runIdleState`, `runReceivingState`, `executeSend`, `handleFrame`,
|
||||
`handleUpstreamMessage`, `recordSensorData`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在每个方法入口处,使用 `newCtx, logger := logs.Trace(ctx, t.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `t.logger.Info`, `t.logger.Errorf`, `t.logger.Warnf`, `t.logger.Infof`, `t.logger.Debugf`
|
||||
的调用替换为 `logger.Info`, `logger.Errorf`, `logger.Warnf`, `logger.Infof`, `logger.Debugf`。
|
||||
- [x] 确保所有对 `t.port`, `t.areaControllerRepo`, `t.pendingCollectionRepo`, `t.deviceRepo`,
|
||||
`t.sensorDataRepo` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/infra/transport/lora/placeholder_transport.go` (`lora.PlaceholderTransport`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `PlaceholderTransport` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPlaceholderTransport`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `PlaceholderTransport` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PlaceholderTransport")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `PlaceholderTransport` 结构体的 `selfCtx` 成员。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(ctx, selfCtx, "NewPlaceholderTransport")` 获取 `logger` 实例,并替换
|
||||
`logger.Info` 调用。
|
||||
- **公共方法改造 (`Listen`, `Stop`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, p.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `p.logger.Warnf` 的调用替换为 `logger.Warnf`。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
### 重构任务清单:纯 Context 驱动的调用链追踪
|
||||
|
||||
---
|
||||
|
||||
#### 1. 核心改造 (`logs` 包)
|
||||
|
||||
- **`internal/infra/logs/logs.go` (`logs.Logger` - 内部实现,非注入)**
|
||||
- **核心包级函数实现 (根据 `implementation.md` 描述)**:
|
||||
- [x] 实现 `AddCompName(ctx context.Context, compName string) context.Context`。
|
||||
- [x] 实现
|
||||
`AddFuncName(upstreamCtx context.Context, selfCtx context.Context, funcName string) context.Context`。
|
||||
- [x] 实现 `GetLogger(ctx context.Context) *Logger`。
|
||||
- [x] 实现
|
||||
`Trace(upstreamCtx context.Context, selfCtx context.Context, funcName string) (context.Context, *Logger)`。
|
||||
- **`Logger` 结构体改造**:
|
||||
- [x] 无
|
||||
- **`GormLogger` 改造**:
|
||||
- [x] 修改 `GormLogger.Info` 方法,从传入的 `ctx` 中获取 `logger` 实例,并使用该实例进行日志记录。
|
||||
- [x] 修改 `GormLogger.Warn` 方法,从传入的 `ctx` 中获取 `logger` 实例,并使用该实例进行日志记录。
|
||||
- [x] 修改 `GormLogger.Error` 方法,从传入的 `ctx` 中获取 `logger` 实例,并使用该实例进行日志记录。
|
||||
- [x] 修改 `GormLogger.Trace` 方法,从传入的 `ctx` 中获取 `logger` 实例,并使用该实例进行日志记录。特别是
|
||||
`With(fields...)` 的调用需要调整。
|
||||
|
||||
---
|
||||
|
||||
#### 2. 依赖注入与结构体改造
|
||||
|
||||
- **`internal/core/application.go`**:
|
||||
- [x] 移除 `Application` 结构体中的 `Logger *logs.Logger` 成员。
|
||||
- [x] 修改 `NewApplication` 函数,使其不再创建 `logger`,而是创建根 `context.Background()`。
|
||||
- [x] 调整 `NewApplication`,将根 `context` 传递给 `initInfrastructure`, `initDomainServices`, `initAppServices` 和
|
||||
`api.NewAPI`。
|
||||
- [x] 移除 `Application` 结构体中所有对 `app.Logger` 的直接调用,改为通过 `context` 获取 `Logger`。
|
||||
|
||||
- **`internal/core/component_initializers.go`**:
|
||||
- [x] **修改所有组件结构体定义**: 遍历所有相关组件(Controllers, Services, Repositories 等),将其结构体中的
|
||||
`logger *logs.Logger` 成员变量替换为 `selfCtx context.Context`。
|
||||
- [x] **重构所有 `init...` 函数**:
|
||||
- 移除所有 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- 在每个 `init...` 函数内部,为即将创建的组件生成其专属的 `selfCtx`。例如:
|
||||
`selfCtx := logs.AddCompName(ctx, 'ComponentName')`。
|
||||
- 将这个 `selfCtx` 注入到组件的构造函数中,并由组件保存为 `selfCtx` 成员。
|
||||
|
||||
---
|
||||
|
||||
#### 3. 全局方法签名改造:传递 `context.Context`
|
||||
|
||||
**以下所有列出的组件,其所有公共方法都需要进行签名改造,将 `ctx context.Context` 作为第一个参数。**
|
||||
|
||||
##### 3.1. API 层
|
||||
|
||||
[api](./task-api.md)
|
||||
[controller](./task-controller.md)
|
||||
[middleware](./task-middleware.md)
|
||||
[webhook](./task-webhook.md)
|
||||
|
||||
##### 3.2. 应用层 (Application Services)
|
||||
|
||||
[service](./task-service.md)
|
||||
|
||||
##### 3.3. 领域层 (Domain Services)
|
||||
|
||||
[domain](./task-domain.md)
|
||||
|
||||
##### 3.4. 基础设施层 (Infrastructure)
|
||||
|
||||
[repository](./task-repository.md)
|
||||
[infra-other](./task-infra.md)
|
||||
|
||||
---
|
||||
|
||||
#### 4. 日志调用点及方法内部逻辑改造
|
||||
|
||||
- [x] **遍历所有业务方法** (针对上述所有列出的组件的公共方法):
|
||||
- [x] **定位旧日志**: 搜索所有对旧 `z.logger.*` 成员的调用。
|
||||
- [x] **改造方法入口** (对于非 Controller 方法):
|
||||
1. 在方法开始处,使用作为参数传入的 `ctx` (作为 `upstreamCtx`) 和组件自身持有的 `z.selfCtx`,调用 `logs.Trace`。
|
||||
- `newCtx, logger := logs.Trace(ctx, z.selfCtx, 'MethodName')`
|
||||
2. 将所有旧的 `z.logger.*(...)` 调用,替换为使用新获取的 `logger.*(...)`。
|
||||
- [x] **改造下游调用**:
|
||||
1. 在方法内部,当需要调用其他组件的方法时(如下游服务),**必须传递 `newCtx`**。
|
||||
- `err := z.downstreamService.DoSomething(newCtx, data)`
|
||||
@@ -0,0 +1,25 @@
|
||||
- **`internal/app/middleware/auth.go`**
|
||||
- **中间件函数改造 (`AuthMiddleware`)**:
|
||||
- [x] 在 `AuthMiddleware` 返回的 `echo.HandlerFunc` 内部,获取 `echo.Context` 的 `request.Context()` 作为
|
||||
`upstreamCtx`。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(upstreamCtx, context.Background(), "AuthMiddleware")` 来创建 `newCtx`
|
||||
和 `logger` 实例。
|
||||
- [x] 使用 `c.SetRequest(c.Request().WithContext(newCtx))` 将更新后的 `newCtx` 写入 `echo.Context`,以便后续处理链使用。
|
||||
- [x] 将所有对 `controller.SendErrorWithStatus` 的调用替换为 `controller.SendErrorWithAudit`。
|
||||
- [x] 确保 `controller.SendErrorWithAudit` 接收 `newCtx` 作为第一个参数,并提供适当的 `actionType`,
|
||||
`description`, `targetResource`。
|
||||
- 例如,对于“请求未包含授权标头”的错误,`actionType` 可以是“认证失败”,`description` 是“请求未包含授权标头”,
|
||||
`targetResource` 为 `nil`。
|
||||
- 对于“无效的Token”错误,`actionType` 可以是“认证失败”,`description` 是“无效的Token”,`targetResource` 是
|
||||
`tokenString`。
|
||||
- [x] 在 `AuthMiddleware` 内部,如果需要日志记录(例如 `tokenService.ParseToken` 失败或 `userRepo.FindByID`
|
||||
失败),使用新创建的 `logger` 实例进行日志输出。
|
||||
- [x] **关键**: 使用 `c.SetRequest(c.Request().WithContext(newCtx))` 将更新后的 `Context` 写回 `echo.Context`
|
||||
,以便传递给后续的中间件和 `Controller`。
|
||||
- **`internal/app/middleware/audit.go`**
|
||||
- **改造动作**:
|
||||
- [x] 检查并重构所有日志记录中间件。
|
||||
- [x] 中间件应该从请求的 `c.Request().Context()` 中提取 `upstreamCtx`。
|
||||
- [x] 使用 `logs.Trace` 或 `logs.AddFuncName` 创建新的 `Context` 和 `Logger`。
|
||||
- [x] **关键**: 使用 `c.SetRequest(c.Request().WithContext(newCtx))` 将更新后的 `Context` 写回 `echo.Context`
|
||||
,以便传递给后续的中间件和 `Controller`。
|
||||
@@ -0,0 +1,396 @@
|
||||
- **`internal/infra/repository/unit_of_work.go` (`repository.UnitOfWork` - `gormUnitOfWork` 实现)**
|
||||
- **接口改造 (`UnitOfWork`)**:
|
||||
- [x] 修改 `UnitOfWork` 接口中的 `ExecuteInTransaction` 方法签名,使其接收 `ctx context.Context` 作为第一个参数:
|
||||
`ExecuteInTransaction(ctx context.Context, fn func(tx *gorm.DB) error) error`。
|
||||
- **结构体改造 (`gormUnitOfWork`)**:
|
||||
- [x] 移除 `gormUnitOfWork` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormUnitOfWork`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `gormUnitOfWork` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "GormUnitOfWork")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormUnitOfWork` 结构体的 `selfCtx` 成员。
|
||||
- **方法改造 (`ExecuteInTransaction`)**:
|
||||
- [x] 修改方法签名,使其接收 `ctx context.Context` 作为第一个参数:
|
||||
`(u *gormUnitOfWork) ExecuteInTransaction(ctx context.Context, fn func(tx *gorm.DB) error) error`。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, u.selfCtx, "ExecuteInTransaction")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `u.logger.Errorf` 的调用替换为 `logger.Errorf`。
|
||||
- [x] 在开启事务时,使用 `tx := u.db.WithContext(newCtx).Begin()`,确保事务 `tx` 携带了正确的上下文。
|
||||
|
||||
- **`internal/infra/repository/plan_repository.go` (`repository.PlanRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPlanRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPlanRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PlanRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPlanRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/user_repository.go` (`repository.UserRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormUserRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormUserRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "UserRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormUserRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/device_repository.go` (`repository.DeviceRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormDeviceRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormDeviceRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "DeviceRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormDeviceRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_pen_repository.go` (`repository.PigPenRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigPenRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigPenRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigPenRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigPenRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_farm_repository.go` (`repository.PigFarmRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigFarmRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigFarmRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigFarmRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigFarmRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_sick_repository.go` (`repository.PigSickLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigSickLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigSickLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigSickLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigSickLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_batch_repository.go` (`repository.PigBatchRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigBatchRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigBatchRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigBatchRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigBatchRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_trade_repository.go` (`repository.PigTradeRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigTradeRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigTradeRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigTradeRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigTradeRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/sensor_data_repository.go` (`repository.SensorDataRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormSensorDataRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormSensorDataRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "SensorDataRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormSensorDataRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/notification_repository.go` (`repository.NotificationRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormNotificationRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormNotificationRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "NotificationRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormNotificationRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pending_task_repository.go` (`repository.PendingTaskRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPendingTaskRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPendingTaskRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PendingTaskRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPendingTaskRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/raw_material_repository.go` (`repository.RawMaterialRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormRawMaterialRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormRawMaterialRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "RawMaterialRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormRawMaterialRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/execution_log_repository.go` (`repository.ExecutionLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormExecutionLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormExecutionLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "ExecutionLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormExecutionLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_batch_log_repository.go` (`repository.PigBatchLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigBatchLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigBatchLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigBatchLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigBatchLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/medication_log_repository.go` (`repository.MedicationLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormMedicationLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormMedicationLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "MedicationLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormMedicationLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/area_controller_repository.go` (`repository.AreaControllerRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormAreaControllerRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormAreaControllerRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "AreaControllerRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormAreaControllerRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/device_template_repository.go` (`repository.DeviceTemplateRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormDeviceTemplateRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormDeviceTemplateRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "DeviceTemplateRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormDeviceTemplateRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/user_action_log_repository.go` (`repository.UserActionLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormUserActionLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormUserActionLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "UserActionLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormUserActionLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_transfer_log_repository.go` (`repository.PigTransferLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigTransferLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigTransferLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigTransferLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigTransferLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/device_command_log_repository.go` (`repository.DeviceCommandLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormDeviceCommandLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormDeviceCommandLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "DeviceCommandLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormDeviceCommandLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pending_collection_repository.go` (`repository.PendingCollectionRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPendingCollectionRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPendingCollectionRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PendingCollectionRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPendingCollectionRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/raw_material_repository.go` (`repository.RawMaterialRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormRawMaterialRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormRawMaterialRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "RawMaterialRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormRawMaterialRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/execution_log_repository.go` (`repository.ExecutionLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormExecutionLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormExecutionLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "ExecutionLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormExecutionLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_batch_log_repository.go` (`repository.PigBatchLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigBatchLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigBatchLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigBatchLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigBatchLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/medication_log_repository.go` (`repository.MedicationLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormMedicationLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormMedicationLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "MedicationLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormMedicationLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/area_controller_repository.go` (`repository.AreaControllerRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormAreaControllerRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormAreaControllerRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "AreaControllerRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormAreaControllerRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/device_template_repository.go` (`repository.DeviceTemplateRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormDeviceTemplateRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormDeviceTemplateRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "DeviceTemplateRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormDeviceTemplateRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/user_action_log_repository.go` (`repository.UserActionLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormUserActionLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormUserActionLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "UserActionLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormUserActionLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(newCtx)`。
|
||||
|
||||
- **`internal/infra/repository/pig_transfer_log_repository.go` (`repository.PigTransferLogRepository`)**
|
||||
- **结构体改造**:
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewGormPigTransferLogRepository`)**:
|
||||
- [x] 修改函数签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在函数内部,为 `gormPigTransferLogRepository` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigTransferLogRepository")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `gormPigTransferLogRepository` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有公共方法)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx := logs.AddFuncName(ctx, r.selfCtx, "MethodName")`。
|
||||
- [x] 确保所有对 `r.db` 和 `tx` 的调用都使用 `r.db.WithContext(newCtx)` 或 `tx.WithContext(new
|
||||
@@ -0,0 +1,113 @@
|
||||
- **`internal/app/service/pig_farm_service.go` (`service.PigFarmService`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `PigFarmService` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- [x] 移除 `PigFarmService` 结构体中的 `repo repository.PigFarmRepository` 成员,改为
|
||||
`repo repository.PigFarmRepository`。
|
||||
- **构造函数改造 (`NewPigFarmService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `PigFarmService` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigFarmService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `PigFarmService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `CreatePigHouse`, `GetPigHouseByID`, `ListPigHouses`, `UpdatePigHouse`, `DeletePigHouse`,
|
||||
`CreatePen`, `GetPenByID`, `ListPens`, `UpdatePen`, `DeletePen`, `UpdatePenStatus`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf` 和 `s.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `s.repo` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
|
||||
- **`internal/app/service/pig_batch_service.go` (`service.PigBatchService`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `PigBatchService` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPigBatchService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `PigBatchService` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PigBatchService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `PigBatchService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `CreatePigBatch`, `GetPigBatch`, `UpdatePigBatch`, `DeletePigBatch`, `ListPigBatches`,
|
||||
`AssignEmptyPensToBatch`, `ReclassifyPenToNewBatch`, `RemoveEmptyPenFromBatch`, `MovePigsIntoPen`, `SellPigs`,
|
||||
`BuyPigs`, `RecordSickPigs`, `RecordSickPigRecovery`, `RecordSickPigDeath`, `RecordSickPigCull`, `RecordDeath`,
|
||||
`RecordCull`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf` 和 `s.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `s.pigBatchRepo`, `s.pigBatchLogRepo`, `s.uow`, `s.transferSvc`, `s.tradeSvc`, `s.sickSvc`
|
||||
等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
|
||||
- **`internal/app/service/monitor_service.go` (`service.MonitorService`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `MonitorService` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewMonitorService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `MonitorService` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "MonitorService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `MonitorService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `ListSensorData`, `ListDeviceCommandLogs`, `ListPlanExecutionLogs`, `ListTaskExecutionLogs`,
|
||||
`ListPendingCollections`, `ListUserActionLogs`, `ListRawMaterialPurchases`, `ListRawMaterialStockLogs`,
|
||||
`ListFeedUsageRecords`, `ListMedicationLogs`, `ListPigBatchLogs`, `ListWeighingBatches`, `ListWeighingRecords`,
|
||||
`ListPigTransferLogs`, `ListPigSickLogs`, `ListPigPurchases`, `ListPigSales`, `ListNotifications`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf` 和 `s.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `s.repo` 的调用都将 `newCtx` 作为第一个参数传递。
|
||||
|
||||
- **`internal/app/service/device_service.go` (`service.DeviceService`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `DeviceService` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewDeviceService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `DeviceService` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "DeviceService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `DeviceService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `CreateDevice`, `GetDevice`, `ListDevices`, `UpdateDevice`, `DeleteDevice`, `ManualControl`,
|
||||
`CreateAreaController`, `GetAreaController`, `ListAreaControllers`, `UpdateAreaController`,
|
||||
`DeleteAreaController`, `CreateDeviceTemplate`, `GetDeviceTemplate`, `ListDeviceTemplates`,
|
||||
`UpdateDeviceTemplate`, `DeleteDeviceTemplate`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf`, `s.logger.Warnf`, `s.logger.Infof` 的调用替换为 `logger.Errorf`,
|
||||
`logger.Warnf`, `logger.Infof`。
|
||||
- [x] 确保所有对 `s.repo`, `s.generalDeviceService` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
|
||||
- **`internal/app/service/plan_service.go` (`service.PlanService`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `PlanService` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPlanService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `PlanService` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "PlanService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `PlanService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (
|
||||
所有方法,例如 `CreatePlan`, `GetPlanByID`, `ListPlans`, `UpdatePlan`, `DeletePlan`, `StartPlan`, `StopPlan`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf` 和 `s.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `s.repo`, `s.planExecutionManager`, `s.analysisPlanTaskManager` 等依赖的调用都将 `newCtx`
|
||||
作为第一个参数传递。
|
||||
|
||||
- **`internal/app/service/user_service.go` (`service.UserService`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `UserService` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewUserService`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `UserService` 创建其专属的 `selfCtx`:`selfCtx := logs.AddCompName(ctx, "UserService")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `UserService` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (所有方法,例如 `CreateUser`, `Login`, `SendTestNotification`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在方法入口处,使用 `newCtx, logger := logs.Trace(ctx, s.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `s.logger.Errorf` 和 `s.logger.Infof` 的调用替换为 `logger.Errorf` 和 `logger.Infof`。
|
||||
- [x] 确保所有对 `s.repo`, `s.tokenService`, `s.notifier` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
@@ -0,0 +1,39 @@
|
||||
- **`internal/app/webhook/chirp_stack.go` (`webhook.ChirpStackListener`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `ChirpStackListener` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewChirpStackListener`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `ChirpStackListener` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "ChirpStackListener")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `ChirpStackListener` 结构体的 `selfCtx` 成员。
|
||||
- **公共方法改造 (`Handler`)**:
|
||||
- [x] `Handler` 方法返回 `http.HandlerFunc`。在返回的 `http.HandlerFunc` 内部,获取 `r.Context()` 作为
|
||||
`upstreamCtx`。
|
||||
- [x] 在 `go c.handler(b, event)` 调用之前,将 `upstreamCtx` 传递给 `c.handler` 方法,即
|
||||
`go c.handler(upstreamCtx, b, event)`。
|
||||
- [x] 将所有对 `c.logger.Errorf` 的调用替换为 `logger.Errorf` (在 `handler` 方法中处理)。
|
||||
- **内部辅助方法改造 (`handler`, `handleUpEvent`, `handleStatusEvent`, `handleAckEvent`, `handleLogEvent`,
|
||||
`handleJoinEvent`, `handleTxAckEvent`, `handleLocationEvent`, `handleIntegrationEvent`, `recordSensorData`)**:
|
||||
- [x] 修改方法签名,添加 `ctx context.Context` 作为第一个参数。
|
||||
- [x] 在每个方法入口处,使用 `newCtx, logger := logs.Trace(ctx, c.selfCtx, "MethodName")` 获取新的
|
||||
`context.Context` 和 `logger` 实例。
|
||||
- [x] 将所有对 `c.logger.Errorf`, `c.logger.Infof`, `c.logger.Warnf` 的调用替换为 `logger.Errorf`,
|
||||
`logger.Infof`, `logger.Warnf`。
|
||||
- [x] 确保所有对 `c.sensorDataRepo`, `c.deviceRepo`, `c.areaControllerRepo`, `c.deviceCommandLogRepo`,
|
||||
`c.pendingCollectionRepo` 等依赖的调用都将 `newCtx` 作为第一个参数传递。
|
||||
- **`internal/app/webhook/placeholder_listener.go` (`webhook.PlaceholderListener`)**
|
||||
- **结构体改造**:
|
||||
- [x] 移除 `PlaceholderListener` 结构体中的 `logger *logs.Logger` 成员。
|
||||
- [x] 新增 `selfCtx context.Context` 成员。
|
||||
- **构造函数改造 (`NewPlaceholderListener`)**:
|
||||
- [x] 修改函数签名,移除 `logger *logs.Logger` 参数,改为接收 `ctx context.Context`。
|
||||
- [x] 在函数内部,为 `PlaceholderListener` 创建其专属的 `selfCtx`:
|
||||
`selfCtx := logs.AddCompName(ctx, "PlaceholderListener")`。
|
||||
- [x] 将这个 `selfCtx` 赋值给 `PlaceholderListener` 结构体的 `selfCtx` 成员。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(ctx, selfCtx, "NewPlaceholderListener")` 获取 `logger` 实例,并替换
|
||||
`logger.Info` 调用。
|
||||
- **公共方法改造 (`Handler`)**:
|
||||
- [x] 在 `Handler` 方法返回的 `http.HandlerFunc` 内部,获取 `r.Context()` 作为 `upstreamCtx`。
|
||||
- [x] 使用 `newCtx, logger := logs.Trace(upstreamCtx, p.selfCtx, "Handler")` 获取 `logger` 实例。
|
||||
- [x] 将所有对 `p.logger.Warn` 的调用替换为 `logger.Warn`。
|
||||
15
design/archive/2025-11-06-health-check-routing/index.md
Normal file
15
design/archive/2025-11-06-health-check-routing/index.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 问题
|
||||
|
||||
增加健康检查路由, 供docker/k8s健康探测服务使用
|
||||
|
||||
## issue
|
||||
|
||||
http://git.huangwc.com/pig/pig-farm-controller/issues/48
|
||||
|
||||
# 方案
|
||||
|
||||
增加两个对应路由
|
||||
|
||||
# 其他修复
|
||||
|
||||
修复系统启动时系统计划更新和触发器刷新冲突的问题
|
||||
@@ -0,0 +1,21 @@
|
||||
# 问题
|
||||
|
||||
每次全量传感器采集都会连着下发五条采集指令
|
||||
|
||||
## issue
|
||||
|
||||
http://git.huangwc.com/pig/pig-farm-controller/issues/55
|
||||
|
||||
# 解决方案
|
||||
|
||||
## 问题描述
|
||||
|
||||
系统启动时没有删掉旧的任务但会在更新时把新的任务插入, 导致计划中的任务越来越多
|
||||
|
||||
## 修复方案
|
||||
|
||||
系统启动更新系统计划时删掉旧的任务
|
||||
|
||||
## 其他修复
|
||||
|
||||
删除任务时对于设备任务关联表是软删除, 但这张表不支持软删除, 遂改为硬删除
|
||||
149
design/archive/2025-11-10-exceeding-threshold-alarm/index.md
Normal file
149
design/archive/2025-11-10-exceeding-threshold-alarm/index.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# 需求
|
||||
|
||||
实现采集数据超过阈值报警
|
||||
|
||||
## issue
|
||||
|
||||
[实现采集数据超过阈值报警](http://git.huangwc.com/pig/pig-farm-controller/issues/62)
|
||||
|
||||
# 方案
|
||||
|
||||
1. **架构核心**: 新增一个 **告警领域服务**,作为告警系统的核心大脑,负责告警事件的生命周期管理。
|
||||
2. **任务分离**:
|
||||
* 新增 **阈值告警任务** (分为区域主控和普通设备两种),仅负责检测数据并将结果报告给领域服务。
|
||||
* 新增 **告警通知发送任务**,作为一个独立的、系统预定义的定时任务,负责调用领域服务,获取并发送所有待处理的通知。
|
||||
3. **计划调度**:
|
||||
* 修改现有 "定时全量数据采集" 计划, 更名为 "周期性系统健康检查"。此计划包含固定的 **全量采集任务** 和由用户动态配置的
|
||||
**阈值告警任务**。
|
||||
* 新增一个独立的 "告警通知发送" 计划,用于定时执行固定的 **告警通知发送任务**。
|
||||
4. **数据与接口**:
|
||||
* 新增独立的告警记录表(建议采用“活跃告警表 + 历史告警超表”的模式)。
|
||||
* 新增相应的告警配置管理接口。
|
||||
|
||||
## 方案细节
|
||||
|
||||
### 架构与职责划分
|
||||
|
||||
1. **告警领域服务 (`internal/domain/alarm/`) - 管理器**
|
||||
* **职责**: 作为告警系统的核心大脑,负责处理告警事件的完整生命周期。
|
||||
* **功能**:
|
||||
* 接收来自检测任务的状态报告(包含设备ID、传感器类型、当前是否异常等信息)。
|
||||
* 根据报告和数据库中的告警记录,决策是创建新告警、更新为已解决、还是因被忽略而跳过。
|
||||
* 管理“手动忽略” (`Ignored`) 状态和忽略到期时间 (`ignored_until`)。
|
||||
* 实现可配置的“重复通知”策略(`re_notification_interval`),决定何时对持续存在的告警再次发送通知。
|
||||
* 提供接口供 `告警通知发送任务` 调用,以获取所有待处理的通知。
|
||||
|
||||
2. **阈值告警任务 (`internal/domain/task/`) - 检测器**
|
||||
* **职责**: 职责纯粹,仅负责执行检测并将结果报告给告警领域服务。
|
||||
* **逻辑**: 从传感器数据表读取最新数据 -> 与自身配置的阈值进行比对 -> 无论结果如何,都调用 `告警领域服务.ReportStatus()`
|
||||
报告当前状态(正常或异常)。
|
||||
* **无状态**: 任务本身不关心告警是否已存在或被忽略,它只负责“状态同步”。
|
||||
|
||||
3. **告警通知发送任务 (`internal/domain/task/`) - 发送器**
|
||||
* **职责**: 作为一个独立的定时任务,解耦通知发送与告警检测。
|
||||
* **逻辑**: 调用 `告警领域服务.GetAndProcessPendingNotifications()` -> 获取待发送通知列表 -> 调用 `通知领域服务`
|
||||
逐一发送。
|
||||
* **优势**: 统一管理定时任务,实现资源控制,提高系统稳定性和可扩展性。
|
||||
|
||||
### 计划与任务调度
|
||||
|
||||
1. **"周期性系统健康检查" 计划**
|
||||
* **任务构成**:
|
||||
* **全量数据采集任务 (ExecutionOrder: 1)**: 系统预定义,必须是第一个执行的任务,为后续的告警检测提供最新的数据基础。
|
||||
* **阈值告警任务 (ExecutionOrder: 2, 3...)**: 由用户通过API动态配置和管理,`告警配置服务` 负责将其增删改到此计划中。
|
||||
|
||||
2. **"告警通知发送" 计划**
|
||||
* **任务构成**: 包含一个系统预定义的 `告警通知发送任务`。
|
||||
* **调度**: 可配置独立的执行频率(如每分钟一次),与健康检查计划解耦。
|
||||
|
||||
3. **系统初始化 (`data_initializer.go`)**
|
||||
* **职责**: 只负责创建和维护系统预定义的、固定的计划和任务。
|
||||
* **操作**:
|
||||
* 确保 "周期性系统健康检查" 计划存在,并包含 `全量数据采集任务`。
|
||||
* 确保 "告警通知发送" 计划存在,并包含 `告警通知发送任务`。
|
||||
* **注意**: 初始化逻辑 **不会** 也 **不应该** 触及用户动态配置的阈值告警任务。
|
||||
|
||||
### 阈值告警任务 (用户可配置的任务类型)
|
||||
|
||||
1. **任务类型**: 提供两种可供用户配置的阈值告警任务类型,分别对应 **区域主控** 和 **普通设备** 告警。
|
||||
2. **参数结构**:
|
||||
* **通用参数**: 任务参数将包含 `Thresholds` (阈值) 和 `Operator` (操作符,如 `>` 或 `<`) 字段。
|
||||
* **普通设备任务**: 配置包含 `DeviceID`。
|
||||
* **区域主控任务**: 配置包含 `AreaControllerID`, `SensorType`, 以及一个 `ExcludeDeviceIDs` (需要排除的设备ID列表)。
|
||||
|
||||
### 告警事件与生命周期
|
||||
|
||||
1. **告警事件定义**:
|
||||
* 区分 **告警规则** (配置的策略) 和 **告警事件** (规则被具体设备触发的实例)。
|
||||
* 区域主控下不同设备触发的告警,即使基于同一规则,也应视为独立的 **告警事件**,以便于精确追溯和独立操作。
|
||||
|
||||
2. **生命周期管理**:
|
||||
* **自动闭环**: 当阈值告警任务报告数据恢复正常时,告警领域服务会自动将对应的 `Active` 告警事件状态更新为 `Resolved`。
|
||||
* **手动忽略 (Snooze)**: 用户可通过接口将告警事件状态置为 `Ignored` 并设置 `ignored_until`
|
||||
。在此期间,即使数据持续异常,也不会发送通知。忽略到期后若问题仍存在,告警将重新变为 `Active` 并发送通知。
|
||||
* **持续告警与重复通知**: 对持续未解决的 `Active` 告警,只保留一条记录。告警领域服务会根据 `re_notification_interval`
|
||||
配置的重复通知间隔,决定是否需要再次发送通知。
|
||||
|
||||
### 数据库设计考量
|
||||
|
||||
1. **冷热分离方案 (推荐)**:
|
||||
* **`active_alarms` (活跃告警表)**:
|
||||
* **类型**: 标准 PostgreSQL 表。
|
||||
* **内容**: 只存放 `Active` 和 `Ignored` 状态的告警。
|
||||
* **优势**: 保证高频读写的性能,避免在被压缩的数据上执行更新操作。
|
||||
* **`historical_alarms` (历史告警表)**:
|
||||
* **类型**: 改造为 **TimescaleDB 超表**。
|
||||
* **内容**: 存放 `Resolved` 状态的告警。当告警在 `active_alarms` 中被解决后,记录将移至此表。
|
||||
* **优势**: 适合存储海量历史数据,便于分析、统计,并可利用 TimescaleDB 的压缩和数据生命周期管理功能。
|
||||
|
||||
2. **表结构字段**:
|
||||
* `status`: 枚举类型,包含 `Active`, `Resolved`, `Ignored`。
|
||||
* `ignored_until`: `timestamp` 类型,记录忽略截止时间。
|
||||
* `last_notified_at`: `timestamp` 类型,记录上次发送通知的时间。
|
||||
|
||||
### 阈值告警服务 (领域层)
|
||||
|
||||
1. **服务职责**:
|
||||
* 负责管理阈值告警 **任务配置** 的增删改查。这些任务配置包含了具体的阈值规则。
|
||||
* 负责将用户创建的阈值告警任务动态更新到 "周期性系统健康检查" 计划中。
|
||||
* **任务配置引用检查**: 提供自检方法,用于在删除设备或设备模板前,检查它们是否被任何阈值告警任务配置所引用,以防止产生悬空引用。
|
||||
|
||||
2. **排除列表计算与联动**:
|
||||
* **删除独立任务配置后归属**: 当一个普通设备的独立告警任务配置被删除时,它将自动从其所属区域主控的 `ExcludeDeviceIDs`
|
||||
列表中移除,从而回归到区域统一告警策略的管理之下。
|
||||
* **设备生命周期管理**: 在对设备进行修改(特别是更换区域主控)或删除时,以及在删除区域主控时,必须同步更新相关的
|
||||
`ExcludeDeviceIDs` 列表,同时解决相关告警(当删除时), 以保证数据一致性。
|
||||
* **实现**: `DeviceService` 中负责处理设备更新和删除的方法,需要调用本服务提供的“任务配置引用检查”和刷新接口。
|
||||
|
||||
### 阈值告警控制器
|
||||
|
||||
1. **独立接口**: 提供两组独立的 Web 接口,分别用于管理区域主控和普通设备的阈值告警配置。
|
||||
* 区域主控告警配置接口: `/api/v1/alarm/region-config`
|
||||
* 普通设备告警配置接口: `/api/v1/alarm/device-config`
|
||||
2. **接口职责**: 接口负责接收前端请求,调用应用服务层的阈值告警服务来完成实际的业务逻辑。
|
||||
|
||||
### TODO
|
||||
|
||||
1. 是否要加一个延时操作, 因为采集是异步的, 采集任务结束时不一定能拿到最新数据, 所以需要一个延时操作等待区域主控上传
|
||||
2. 统一一下区域主控的命名, 目前有AreaController和RegionalController, 不排除还有别的
|
||||
3. 将数据类型转为float32, 节约空间, float64精度有些浪费, float32小数点后6-7位足够了
|
||||
|
||||
# 实现记录
|
||||
|
||||
1. 定义告警表和告警历史表
|
||||
2. 重构部分枚举, 让models包不依赖其他项目中的包
|
||||
3. 创建仓库层对象(不包含方法)
|
||||
4. 实现告警发送任务
|
||||
5. 实现告警通知发送计划/全量采集计划改名
|
||||
6. 实现设备阈值检查任务
|
||||
7. 实现忽略告警和取消忽略告警接口及功能
|
||||
8. 实现列表查询活跃告警和历史告警
|
||||
9. 系统初始化时健康计划调整(包括增加延时任务)
|
||||
10. 实现区域阈值告警任务
|
||||
11. 实现区域阈值告警和设备阈值告警的增删改查
|
||||
12. 实现任务11应的八个web接口
|
||||
13. 实现根据区域ID或设备ID清空对应阈值告警任务
|
||||
14. 设备和区域主控删除时清除对应区域阈值告警或设备阈值告警任务
|
||||
15. 将所有Regional更改为Area
|
||||
16. float64全部改float32
|
||||
17. uint/uint64全部改为uint32
|
||||
82
design/archive/2025-11-29-recipe-management/index.md
Normal file
82
design/archive/2025-11-29-recipe-management/index.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# 需求
|
||||
|
||||
饲料配方管理及自动生成配方
|
||||
|
||||
## issue
|
||||
|
||||
http://git.huangwc.com/pig/pig-farm-controller/issues/66
|
||||
|
||||
# 开发计划
|
||||
|
||||
1. 原料营养价值管理
|
||||
- 增删改查
|
||||
- 内置60+条常用原料(玉米、豆粕43、豆粕46、发酵豆粕、麸皮、次粉、DDGS、乳清粉、鱼粉、膨化大豆、各种氨基酸、预混料、油脂等)
|
||||
- 每种原料固定营养值(消化能、粗蛋白、赖氨酸、钙、磷等15项左右)
|
||||
|
||||
2. 饲料库存管理(代替批次)
|
||||
- 字段:饲料名、当前原料种类、当前剩余量(吨)、上次入料日期、保质期剩余天数(手动填)、是否发酵料(勾选)
|
||||
- 发酵料塔额外字段:
|
||||
- 发酵状态(未发酵 / 正在发酵 / 已发酵可用)
|
||||
- 发酵开始日期
|
||||
- 发酵几天(默认3~7天)
|
||||
- 水分增加比例(默认+10~20%)
|
||||
- 营养折损系数(可调,粗蛋白-5%、能量-3%之类)
|
||||
|
||||
3. 猪只阶段营养需求管理
|
||||
- 预设10个常用阶段(教槽、仔猪、小猪、中猪、大猪、后备、怀孕前中后、哺乳)
|
||||
- 每个阶段维护营养需求上下限(消化能、粗蛋白、赖氨酸、钙、有效磷等12项)
|
||||
|
||||
4. 配方管理
|
||||
- 按阶段建配方
|
||||
- 支持增删改查 + 复制上个配方快速新建
|
||||
- 配方明细:原料 + 配比(%)
|
||||
|
||||
5. 自动生成配方(核心功能)
|
||||
- 选择阶段 → 点击“自动计算最低成本配方”
|
||||
- 自动读取当前所有料塔的:
|
||||
- 剩余量(不够的原料自动降配比)
|
||||
- 保质期剩余天数(越快过期的优先用,权重×1.5)
|
||||
- 发酵料塔如果状态是“已发酵可用”则按发酵后营养值参与计算
|
||||
- 输出:总成本、营养达标情况、发酵料占比、即将过期原料使用提示
|
||||
|
||||
6. 配方下发与记录
|
||||
- 一键下发到喂料站/料线(生成下料曲线)
|
||||
- 自动记录今天用了哪个配方
|
||||
|
||||
7. 简单查看功能
|
||||
- 两个配方对比页面(营养+成本对比)
|
||||
|
||||
# 实现总结
|
||||
|
||||
## 实现内容
|
||||
|
||||
实现库存和原料和营养和猪营养需求的管理, 支持根据库存和已录入原料和猪营养需求生成配方
|
||||
|
||||
## TODO
|
||||
|
||||
1. 发酵料管理考虑到发酵目前没有自动化流程, 不好追踪, 遂暂时不做
|
||||
2. 目前的价格是根据原料的参考价设置的, 后续应当实现一个在服务供平台采集参考价, 以及使用原料采购价计算
|
||||
3. 原料应该加上膨润土等, 比如膨润土的黄曲霉素含量应该是负数以表示减少饲料里的含量
|
||||
4. 饲料保质期考虑到批次间管理暂时不方便, 等可以实现同一原料先进先出后再实现
|
||||
5. 暂时不支持指定原料列表然后自动生成, 也不支持告诉用户当前生成不出是为什么, 等以后再做
|
||||
|
||||
# 完成事项
|
||||
|
||||
1. 定义原料表, 营养表, 原料营养表, 原料库存变更表
|
||||
2. 迁移配置文件, 实现从json文件中读取原材料营养预设值, 并自动写入数据库
|
||||
3. 定义配方领域, 实现营养元素的增删改查
|
||||
4. 实现原材料的增删改查和仓库层的原料库存记录表增查
|
||||
5. 定义猪的模型和营养需求模型
|
||||
6. 实现从json读取猪营养需求并写入数据库
|
||||
7. 实现配方领域关于猪模型和营养需求的增删改查
|
||||
8. 实现配方领域的web接口
|
||||
9. 实现修改原料营养信息
|
||||
10. 实现修改猪营养需求
|
||||
11. 配方模型定义和仓库层增删改查方法
|
||||
12. 配方领域层方法
|
||||
13. 重构配方领域
|
||||
14. 配方增删改查服务层和控制器
|
||||
15. 实现库存管理相关逻辑
|
||||
16. 实现配方生成器
|
||||
17. 实现使用系统中所有可用的原料一键生成配方
|
||||
18. 实现优先使用库存的配方一键生成
|
||||
20
design/ota-upgrade-and-log-monitoring/index.md
Normal file
20
design/ota-upgrade-and-log-monitoring/index.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 需求
|
||||
|
||||
支持主控设备ota升级和远程查看日志
|
||||
|
||||
## issue
|
||||
|
||||
http://git.huangwc.com/pig/pig-farm-controller/issues/71
|
||||
|
||||
# 开发计划
|
||||
|
||||
## OTA 升级
|
||||
|
||||
- [x] 增加一个proto对象, 用于封装ota升级包
|
||||
- [x] 区域主控增加版本号
|
||||
- [x] 增加ping指令并获取带版本号的响应
|
||||
- [ ] [实现ota升级逻辑](design/ota-upgrade-and-log-monitoring/ota_upgrade_solution.md)
|
||||
|
||||
## Lora 监听逻辑重构
|
||||
|
||||
- [x] [Lora逻辑重构](design/ota-upgrade-and-log-monitoring/lora_refactoring_plan.md)
|
||||
199
design/ota-upgrade-and-log-monitoring/lora_refactoring_plan.md
Normal file
199
design/ota-upgrade-and-log-monitoring/lora_refactoring_plan.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# LoRa 通信层统一重构方案
|
||||
|
||||
## 1. 目标
|
||||
|
||||
统一项目当前并存的两种 LoRa 通信模式(基于 ChirpStack API 和基于串口透传),使其在架构层面遵循相同的接口和设计模式。最终实现:
|
||||
|
||||
- **业务逻辑统一**:所有上行业务处理逻辑集中在一个地方,与具体的通信方式无关。
|
||||
- **发送接口统一**:上层服务使用同一个接口发送下行指令,无需关心底层实现。
|
||||
- **架构清晰**:明确划分基础设施层(负责传输)和应用层(负责业务)的职责,并确保正确的依赖方向 (`app` -> `infra`)。
|
||||
- **高扩展性**:未来支持新的通信方式时,只需添加新的“适配器”,而无需改动核心业务代码。
|
||||
|
||||
## 2. 背景与问题分析
|
||||
|
||||
### 2.1. 当前存在两种 LoRa 通信模式
|
||||
|
||||
1. **ChirpStack 模式**: 通过 `internal/infra/transport/lora/chirp_stack.go` 实现发送,通过 `internal/app/listener/chirp_stack/chirp_stack.go` 监听并处理 ChirpStack Webhook 推送的数据。
|
||||
2. **串口透传模式**: 通过 `internal/infra/transport/lora/lora_mesh_uart_passthrough_transport.go` 实现发送和接收处理。
|
||||
|
||||
### 2.2. 核心差异
|
||||
|
||||
| 特性 | ChirpStack 模式 | 串口透传模式 |
|
||||
| :--- | :--- | :--- |
|
||||
| **通信模型** | 双向、有状态、异步API调用 | 单向、无状态、直接串口读写 |
|
||||
| **接收机制** | Webhook (HTTP POST) 推送 | 主动从串口读取字节流 |
|
||||
| **数据格式** | JSON 包装 + Base64 编码 | 自定义二进制物理帧 |
|
||||
| **寻址方式**| `DevEui` | 自定义 16 位网络地址 |
|
||||
| **核心职责** | LNS,管理会话、ACK、队列 | 纯粹的“无线串口” |
|
||||
|
||||
### 2.3. 问题
|
||||
|
||||
- **业务逻辑分散**:处理 `CollectResult` 的业务逻辑在 `chirp_stack.go` 和 `lora_mesh_uart_passthrough_transport.go` 中都存在,造成代码重复和维护困难。
|
||||
- **职责不清**:`lora_mesh_uart_passthrough_transport.go` 同时承担了基础设施(串口读写)和应用(处理业务)两种职责。
|
||||
- **依赖关系混乱**:为了让 `infra` 层的串口模块能调用业务逻辑,可能会导致 `infra` 层反向依赖 `app` 层,破坏了项目的核心架构原则。
|
||||
|
||||
## 3. 统一架构设计方案
|
||||
|
||||
### 3.1. 核心思想
|
||||
|
||||
采用 **端口与适配器模式 (Ports and Adapters Pattern)**,严格遵守 **依赖倒置原则**。
|
||||
|
||||
- **端口 (Port)**:在 `infra` 层定义一个 `UpstreamHandler` 接口。这个接口是 `infra` 层向上层暴露的“端口”,它规定了上行业务处理器必须满足的协约。
|
||||
- **适配器 (Adapter)**:在 `app` 层创建一个 `LoRaListener` 作为“适配器”,它实现 `infra` 层定义的 `UpstreamHandler` 接口,并封装所有核心业务处理逻辑。
|
||||
- **依赖注入**:在系统启动时,将 `app` 层的 `LoRaListener` 实例注入到需要它的 `infra` 层组件中。
|
||||
|
||||
### 3.2. 统一接口定义
|
||||
|
||||
#### 3.2.1. 发送接口 (已存在,无需修改)
|
||||
|
||||
```go
|
||||
// file: internal/infra/transport/transport.go
|
||||
package transport
|
||||
|
||||
type Communicator interface {
|
||||
Send(ctx context.Context, address string, payload []byte) (*SendResult, error)
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.2. 接收处理接口 (端口定义)
|
||||
|
||||
此接口定义了 `infra` 层对上行业务处理器的期望,是 `infra` 层向上层暴露的“端口”。
|
||||
|
||||
```go
|
||||
// file: internal/infra/transport/transport.go
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport/proto"
|
||||
)
|
||||
|
||||
// UpstreamHandler 定义了处理所有来源的上行数据的统一协约。
|
||||
// 任何实现了上行消息监听的基础设施(如串口、MQTT客户端),都应该在收到消息后调用此接口的实现者。
|
||||
// 这样,基础设施层只负责“接收和解析”,而将“业务处理”的控制权交给了上层。
|
||||
type UpstreamHandler interface {
|
||||
// HandleInstruction 处理来自设备的、已解析为Instruction的业务指令。
|
||||
HandleInstruction(ctx context.Context, sourceAddr string, instruction *proto.Instruction) error
|
||||
|
||||
// HandleStatus 处理非业务指令的设备状态更新,例如信号强度、电量等。
|
||||
HandleStatus(ctx context.Context, sourceAddr string, status map[string]interface{}) error
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3. 组件职责划分 (重构后)
|
||||
|
||||
#### 3.3.1. 统一业务处理器 (应用层适配器)
|
||||
|
||||
- **文件**: `internal/app/listener/lora_listener.go` (新)
|
||||
- **职责**:
|
||||
- 实现 `transport.UpstreamHandler` 接口。
|
||||
- 包含所有处理业务所需的依赖(如领域服务、仓储等)。
|
||||
- 实现 `HandleInstruction` 方法,通过 `switch-case` 编排所有核心业务。
|
||||
- 实现 `HandleStatus` 方法,处理设备状态更新。
|
||||
- **这是项目中唯一处理 LoRa 上行业务的地方。**
|
||||
|
||||
#### 3.3.2. 基础设施层 (Infra Layer)
|
||||
|
||||
- **文件 1**: `internal/app/listener/chirp_stack/chirp_stack.go` (重构)
|
||||
- **职责**: 纯粹的 Webhook 适配器。
|
||||
- 移除所有业务逻辑和数据库依赖。
|
||||
- 依赖 `transport.UpstreamHandler` 接口。
|
||||
- 功能:接收 Webhook -> 解析 JSON -> 调用 `handler.HandleInstruction` 或 `handler.HandleStatus`。
|
||||
|
||||
- **文件 2**: `internal/infra/transport/lora/lora_mesh_uart_passthrough_transport.go` (重构)
|
||||
- **职责**: 纯粹的串口传输工具。
|
||||
- 移除所有业务逻辑和数据库依赖。
|
||||
- 依赖 `transport.UpstreamHandler` 接口。
|
||||
- 功能:管理串口 -> 读字节流 -> 重组分片 -> 解析 `proto.Instruction` -> 调用 `handler.HandleInstruction`。
|
||||
|
||||
### 3.4. 架构图 (重构后)
|
||||
|
||||
```
|
||||
+--------------------------------+
|
||||
| Upper-Level Services |
|
||||
| (e.g., DeviceService) |
|
||||
+--------------------------------+
|
||||
|
|
||||
v (uses)
|
||||
+--------------------------------+
|
||||
| transport.Communicator (I) | <-- Infra Layer (Send Port)
|
||||
+--------------------------------+
|
||||
^ ^
|
||||
| | (implements)
|
||||
+------------------+------------------+
|
||||
| ChirpStackSender | UartSender | <-- Infra Layer (Senders)
|
||||
+------------------+------------------+
|
||||
|
||||
|
||||
+--------------------------------+
|
||||
| listener.LoRaListener | <-- App Layer (Adapter)
|
||||
| (Implements UpstreamHandler) |
|
||||
+--------------------------------+
|
||||
^
|
||||
| (dependency, via interface)
|
||||
+--------------------------------+
|
||||
| transport.UpstreamHandler (I) | <-- Infra Layer (Receive Port)
|
||||
+--------------------------------+
|
||||
^ ^
|
||||
| | (calls)
|
||||
+------------------+------------------+
|
||||
| ChirpStackWebhook| UartPassthrough | <-- Infra Layer (Receivers)
|
||||
+------------------+------------------+
|
||||
^ ^
|
||||
| | (receives from)
|
||||
+------------------+------------------+
|
||||
| HTTP Webhook | Serial Port |
|
||||
+------------------+------------------+
|
||||
```
|
||||
|
||||
### 3.5. 依赖注入与组装示例
|
||||
|
||||
```go
|
||||
// file: internal/core/component_initializers.go
|
||||
|
||||
// 1. 创建统一的业务处理器 (App层适配器)
|
||||
// 它实现了 infra 层的 transport.UpstreamHandler 接口
|
||||
loraListener := listener.NewLoRaListener(logger, dbRepo1, dbRepo2)
|
||||
|
||||
// 2. 初始化 ChirpStack 模式
|
||||
// 2a. 创建 ChirpStack 的发送器 (infra)
|
||||
chirpStackCommunicator := chirp_stack.NewChirpStackTransport(...)
|
||||
// 2b. 创建 ChirpStack 的监听器 (infra),并注入 App 层的业务处理器
|
||||
chirpStackListener := chirp_stack.NewChirpStackListener(loraListener)
|
||||
// 2c. 注册 Webhook 路由
|
||||
api.RegisterWebhook("/chirpstack", chirpStackListener.Handler())
|
||||
|
||||
// 3. 初始化串口透传模式
|
||||
// 3a. 创建串口的传输工具 (infra),并注入 App 层的业务处理器
|
||||
uartTransport := lora.NewLoRaMeshUartPassthroughTransport(port, loraListener)
|
||||
// 3b. 启动串口监听
|
||||
uartTransport.Listen()
|
||||
|
||||
// 4. 向上层业务提供统一的发送器
|
||||
var finalCommunicator transport.Communicator
|
||||
if config.UseChirpStack {
|
||||
finalCommunicator = chirpStackCommunicator
|
||||
} else {
|
||||
finalCommunicator = uartTransport
|
||||
}
|
||||
// 将 finalCommunicator 注入到需要发送指令的服务中...
|
||||
```
|
||||
|
||||
## 4. 实施步骤
|
||||
|
||||
1. **定义端口**: 在 `internal/infra/transport/transport.go` 中定义 `UpstreamHandler` 接口。
|
||||
2. **创建适配器**: 创建 `internal/app/listener/lora_listener.go`,定义 `LoRaListener` 结构体,并实现 `transport.UpstreamHandler` 接口。
|
||||
3. **迁移业务逻辑**: 将 `chirp_stack.go` 和 `lora_mesh_uart_passthrough_transport.go` 中的业务逻辑(查库、存数据等)逐步迁移到 `lora_listener.go` 的对应方法中。
|
||||
4. **重构基础设施**:
|
||||
- 清理 `chirp_stack.go`,移除 Repo 依赖,改为依赖 `transport.UpstreamHandler` 接口,并调用其方法。
|
||||
- 清理 `lora_mesh_uart_passthrough_transport.go`,做同样的操作。
|
||||
5. **更新依赖注入**: 修改 `component_initializers.go`,按照 `3.5` 中的示例完成组件的创建和注入。
|
||||
6. **测试与验证**: 对两种模式分别进行完整的上下行通信测试。
|
||||
|
||||
## 5. 收益
|
||||
|
||||
- **消除代码重复**:业务逻辑仅存在于一处。
|
||||
- **职责清晰**:基础设施层只管传输,应用层只管业务。
|
||||
- **正确的依赖关系**:确保了 `app` -> `infra` 的单向依赖,核心架构更加稳固。
|
||||
- **可维护性**:修改业务逻辑只需改一个文件,修改传输细节不影响业务。
|
||||
- **可测试性**:可以轻松地对 `LoRaListener` 进行单元测试,无需真实的硬件或网络。
|
||||
306
design/ota-upgrade-and-log-monitoring/ota_upgrade_solution.md
Normal file
306
design/ota-upgrade-and-log-monitoring/ota_upgrade_solution.md
Normal file
@@ -0,0 +1,306 @@
|
||||
# 区域主控 MicroPython OTA 升级方案
|
||||
|
||||
## 1. 概述
|
||||
|
||||
### 1.1. 目标
|
||||
|
||||
实现区域主控 (ESP32-S3-N16R8, MicroPython 固件) 的安全、可靠的远程固件升级 (OTA)。
|
||||
|
||||
### 1.2. 核心思想
|
||||
|
||||
* **AB 分区模式**: 区域主控采用 AB 分区模式,允许在设备运行时更新非活动分区,升级失败时可回滚到上一个已知的工作版本。
|
||||
* **平台主导**: 升级过程由平台完全控制,包括固件准备、文件分发和升级指令下发。
|
||||
* **LoRa 传输层自动分片**: 充分利用 LoRa 传输层自动分片和重组的能力,简化应用层协议设计。
|
||||
* **逐文件校验**: 设备在接收每个文件后立即进行 MD5 校验,确保文件完整性,并处理重试。
|
||||
* **清单文件**: 使用清单文件管理所有待更新文件的元数据和校验信息。
|
||||
* **设备自驱动**: 设备主动请求清单文件和固件文件,并在所有文件校验成功后自行激活新固件并重启。
|
||||
* **平台记录升级任务**: 平台将记录 OTA 升级任务的创建、进度和最终状态。
|
||||
* **配置文件独立管理**: OTA 升级过程将不涉及配置文件的更新,配置文件由平台提供独立的远程修改功能。
|
||||
|
||||
### 1.3. 涉及组件
|
||||
|
||||
* **平台**: 负责固件包管理、清单文件生成、数字签名(未来)、文件分发、指令下发、状态接收和**升级任务记录**。
|
||||
* **LoRa 传输层**: 负责应用层数据的分片、传输和重组。
|
||||
* **区域主控 (ESP32-S3-N16R8)**: 负责接收文件、存储到非活动分区、文件校验、分区切换、新固件启动验证和状态上报。
|
||||
|
||||
## 2. 固件包结构与准备
|
||||
|
||||
### 2.1. 原始固件包 (由开发者提供给平台)
|
||||
|
||||
* 一个标准的压缩包(例如 `.zip`),其中包含所有 MicroPython `.py` 文件、资源文件等。
|
||||
* 压缩包内的文件结构应与期望在设备上部署的路径结构一致。
|
||||
|
||||
### 2.2. 平台处理流程
|
||||
|
||||
1. **接收**: 平台接收开发者上传的 MicroPython 项目压缩包。
|
||||
2. **解压**: 平台将该压缩包解压到内部的一个临时目录。
|
||||
3. **分析与生成清单**: 平台遍历解压后的所有文件,为每个文件计算:
|
||||
* 在设备上的目标路径 (`path`)
|
||||
* MD5 校验和 (`md5`)
|
||||
* 文件大小 (`size`)
|
||||
* **排除配置文件**: 平台会识别配置文件(例如通过文件名约定,如 `/config/` 目录下的所有文件),并**排除**
|
||||
这些文件,不将其包含在清单文件中,也不通过 OTA 传输。
|
||||
4. **生成清单文件**: 平台根据上述信息,生成一个 JSON 格式的清单文件。
|
||||
5. **数字签名 (未来扩展)**: 平台使用其私钥对**清单文件**的内容进行数字签名,并将签名添加到清单文件中。
|
||||
|
||||
### 2.3. 清单文件 (Manifest File) 结构
|
||||
|
||||
清单文件是一个 JSON 对象,包含新固件的元数据和所有文件的详细信息。
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.1",
|
||||
// 新固件版本号
|
||||
"signature": "...",
|
||||
// 清单文件内容的数字签名 (未来扩展)
|
||||
"files": [
|
||||
{
|
||||
"path": "/manifest.json",
|
||||
// 清单文件本身也作为文件列表的一部分
|
||||
"md5": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
|
||||
"size": 1024
|
||||
},
|
||||
{
|
||||
"path": "/main.py",
|
||||
"md5": "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a1",
|
||||
"size": 10240
|
||||
},
|
||||
{
|
||||
"path": "/lib/sensor.py",
|
||||
"md5": "c3d4e5f6a7b8c9d0e1f2a3b4c5d6a1b2",
|
||||
"size": 5120
|
||||
}
|
||||
// ... 更多文件 (不包含配置文件)
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 通信协议定义 (Protobuf Messages)
|
||||
|
||||
以下是 OTA 过程中平台与区域主控之间通信所需的 Protobuf 消息定义。
|
||||
|
||||
```protobuf
|
||||
// OTA 升级指令和状态消息
|
||||
|
||||
// PrepareUpdateReq: 平台发送给设备,通知设备准备开始 OTA 升级
|
||||
message PrepareUpdateReq {
|
||||
string version = 1; // 新固件版本号
|
||||
string task_id = 2; // 升级任务唯一ID
|
||||
string manifest_md5 = 3; // 清单文件的 MD5 校验和,用于设备初步校验清单文件完整性
|
||||
}
|
||||
|
||||
// RequestFile: 设备向平台请求特定文件 (包括清单文件和固件文件)
|
||||
message RequestFile {
|
||||
string task_id = 1; // 升级任务ID
|
||||
string filepath = 2; // 请求的文件路径 (例如 "/manifest.json" 或 "/main.py")
|
||||
uint32 retry_count = 3; // 设备请求该文件的重试次数
|
||||
}
|
||||
|
||||
// FileResponse: 平台响应设备请求,发送单个文件的完整内容
|
||||
// LoRa 传输层会自动处理分片和重组,因此应用层可以直接发送完整的单个文件内容
|
||||
message FileResponse {
|
||||
string task_id = 1; // 升级任务ID
|
||||
string filepath = 2; // 设备上的目标路径 (例如 "/manifest.json" 或 "/main.py")
|
||||
bytes content = 3; // 文件的完整内容
|
||||
}
|
||||
|
||||
// UpdateStatusReport: 设备向平台报告升级状态
|
||||
message UpdateStatusReport {
|
||||
string device_id = 1; // 设备ID
|
||||
string task_id = 2; // 升级任务ID
|
||||
string current_version = 3; // 操作完成后的当前版本
|
||||
enum Status {
|
||||
STATUS_UNKNOWN = 0;
|
||||
|
||||
// --- 设备主动上报的状态 ---
|
||||
SUCCESS = 1; // 升级成功,新固件已运行 (由设备在自检成功后主动上报)
|
||||
SUCCESS_ALREADY_UP_TO_DATE = 2; // 版本已是最新,未执行升级 (由设备在版本检查后主动上报)
|
||||
FAILED_PRE_CHECK = 3; // 升级前检查失败 (例如拒绝降级、准备分区失败等,由设备主动上报)
|
||||
FAILED_DOWNLOAD = 4; // 文件下载或校验失败 (由设备在下载过程中主动上报)
|
||||
|
||||
// --- 平台推断的状态 (数据库记录用) ---
|
||||
FAILED_TIMEOUT = 5; // 平台在超时后仍未收到SUCCESS报告,将任务标记为此状态
|
||||
}
|
||||
Status status = 4; // 升级的最终状态
|
||||
string error_message = 6; // 人类可读的详细错误信息
|
||||
string failed_file = 7; // 失败时关联的文件路径 (可选)
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 平台侧操作流程
|
||||
|
||||
### 4.1. 准备升级任务
|
||||
|
||||
1. 接收开发者提供的 MicroPython 项目压缩包。
|
||||
2. 解压压缩包。
|
||||
3. 遍历解压后的文件,计算每个文件的 MD5、大小,并确定目标路径。
|
||||
4. **排除配置文件**: 平台会识别配置文件(例如通过文件名约定,如 `/config/` 目录下的所有文件),并**排除**这些文件。
|
||||
5. 生成清单文件 (Manifest File)。**注意:清单文件本身也应作为 OTA 的一部分,其元数据应包含在清单文件自身的 `files`
|
||||
列表中。Manifest文件生成后将被放在解压后的文件夹的根目录下, 方便后续主控设备获取**
|
||||
6. (未来扩展)对清单文件进行数字签名。
|
||||
7. 将清单文件和所有固件文件存储在平台内部,等待分发。
|
||||
8. **记录 OTA 升级任务**: 在数据库中创建一条新的 OTA 升级任务记录(模型名为 `OTATask`,位于 `internal/infra/models/ota.go`
|
||||
),包含任务 ID、目标设备、新固件版本、状态(例如“待开始”)。
|
||||
|
||||
### 4.2. 发送“准备更新”指令
|
||||
|
||||
1. 平台向目标区域主控发送 `PrepareUpdateReq` 消息。
|
||||
2. **更新任务记录**: 平台发送指令后,更新 OTA 任务记录的状态为“进行中”。
|
||||
|
||||
### 4.3. 响应设备文件请求
|
||||
|
||||
1. 平台接收区域主控发送的 `RequestFile` 消息。
|
||||
2. 平台根据 `task_id` 和 `filepath` 在内部存储中找到对应的文件内容。
|
||||
3. 平台构建 `FileResponse` 消息,将文件的完整内容和路径放入其中。
|
||||
4. 平台通过 LoRa 传输层发送 `FileResponse` 消息。
|
||||
|
||||
### 4.4. 处理设备状态上报
|
||||
|
||||
1. 平台接收区域主控发送的 `UpdateStatusReport` 消息。
|
||||
2. 根据报告的 `status` (`SUCCESS` 或 `FAILED`),更新 OTA 任务记录的最终状态,并记录 `error_code` 和 `error_message`。
|
||||
3. 如果状态为 `SUCCESS`,平台应更新该设备在系统中的固件版本记录。
|
||||
4. **总超时管理**: 平台为每个 OTA 任务设置一个总的超时时间(例如 2 小时)。如果在总超时时间内未能收到设备的最终状态报告,平台应自动将该任务标记为
|
||||
`FAILED`,`error_code` 设为 `ERR_TIMEOUT`。
|
||||
5. **处理重复报告**: 平台在收到最终状态报告后,即使后续再次收到相同的报告,也只需更新一次任务记录,无需重复处理。
|
||||
|
||||
## 5. 区域主控侧操作流程 (MicroPython)
|
||||
|
||||
### 5.1. 接收“准备更新”指令与版本检查
|
||||
|
||||
1. 区域主控接收 `PrepareUpdateReq` 消息。
|
||||
2. **版本检查**: 设备将 `PrepareUpdateReq` 中的 `version` 与自身当前运行的固件版本进行比较。
|
||||
* **降级场景**: 如果 `新版本 < 当前版本`,设备立即中止升级,并向平台发送 `UpdateStatusReport` (status: `FAILED`,
|
||||
error_code: `ERR_VERSION_ROLLBACK`, error_message: "拒绝版本回滚,目标版本低于当前版本")。
|
||||
* **同版本场景**: 如果 `新版本 == 当前版本`,设备立即中止升级,并向平台发送 `UpdateStatusReport` (status: `SUCCESS`,
|
||||
error_code: `SUCCESS_ALREADY_UP_TO_DATE`, error_message: "版本已是最新,无需升级")。
|
||||
* **正常升级场景**: 如果 `新版本 > 当前版本`,继续执行下一步。
|
||||
3. **清空非活动分区**: 使用 MicroPython 的文件系统操作(例如 `os.remove()` 和 `os.rmdir()`),递归删除非活动 OTA 分区(例如
|
||||
`/ota_b`)下的所有文件和目录。
|
||||
* **错误处理**: 如果清空分区失败,设备应立即中止,并向平台发送 `UpdateStatusReport` (status: `FAILED`, error_code:
|
||||
`ERR_PREPARE`, error_message: "清空非活动分区失败: [具体错误]").
|
||||
4. 设备准备就绪后,将直接开始请求清单文件。
|
||||
|
||||
### 5.2. 请求并验证清单文件
|
||||
|
||||
1. 设备完成准备后,向平台发送 `RequestFile` 消息,请求清单文件 (`filepath: "/manifest.json"`)。
|
||||
2. 设备接收平台响应的 `FileResponse` 消息,并将其写入非活动分区(例如 `/ota_b/manifest.json`)。
|
||||
3. **MD5 校验**: 计算写入的清单文件的 MD5,并与 `PrepareUpdateReq` 消息中提供的 `manifest_md5` 进行比对。
|
||||
4. **解析 JSON**: 解析清单文件内容。
|
||||
5. **数字签名验证 (未来扩展)**: 使用预置的平台公钥,验证清单文件的数字签名。
|
||||
6. 如果上述任何步骤失败,设备应向平台发送 `UpdateStatusReport` (status: `FAILED`, error_code: `ERR_MANIFEST_VERIFY`,
|
||||
error_message: "[具体失败原因]"), 然后中止升级。
|
||||
|
||||
### 5.3. 请求与存储固件文件 (逐文件校验)
|
||||
|
||||
1. 设备成功接收并验证清单文件后,根据清单文件中的文件列表,**逐个文件**地向平台发送 `RequestFile` 消息。
|
||||
2. 对于每个请求的文件:
|
||||
* **请求、接收与写入**: 设备请求文件,接收响应,并根据 `filepath` 将内容写入到非活动 OTA 分区。需要确保目标目录存在,如果不存在则创建。
|
||||
* **MD5 校验**: 在文件写入完成后,计算该文件的 MD5 校验和,并与清单文件中记录的 MD5 进行比对。
|
||||
* **错误处理与重试**:
|
||||
* 如果文件下载超时、写入失败或 MD5 校验失败,设备将进行重试(例如最多 3 次)。
|
||||
* 如果达到最大重试次数仍失败,设备应立即中止整个 OTA 任务,并向平台发送 `UpdateStatusReport` (status: `FAILED`,
|
||||
error_code: `ERR_DOWNLOAD` 或 `ERR_VERIFY`, error_message: "[具体失败原因]", failed_file: "[失败的文件路径]")。
|
||||
|
||||
### 5.4. 自激活与重启
|
||||
|
||||
1. **所有文件接收并校验成功后**,设备将自行执行以下操作:
|
||||
* **配置 OTA 分区**: 使用 MicroPython 提供的 ESP-IDF OTA API,设置下一个启动分区为刚刚写入新固件的非活动分区。
|
||||
* **自触发重启**: 在成功配置 OTA 分区后,区域主控自行触发重启。
|
||||
|
||||
### 5.5. 新版本启动与验证
|
||||
|
||||
1. 设备重启后,启动加载器会从新的 OTA 分区加载 MicroPython 固件。
|
||||
2. **自检**: 新固件启动后,应执行必要的自检(如 LoRa 初始化、网络连接等)。
|
||||
3. **标记有效**: 只有当所有自检项都成功通过后,新固件才必须调用相应的 API(例如 `esp.ota_mark_app_valid_cancel_rollback()`
|
||||
)来标记自身为有效。
|
||||
4. **看门狗与回滚**:
|
||||
* 如果新固件在一定次数的尝试后仍未标记自身为有效,启动加载器会自动回滚到上一个有效固件。
|
||||
* 在 MicroPython 应用层,如果自检失败,**绝不能**标记自身为有效,并应等待底层机制自动触发回滚。
|
||||
|
||||
### 5.6. 报告最终状态
|
||||
|
||||
1. **成功场景**: 新固件自检成功并标记有效后,向平台发送 `UpdateStatusReport` (status: `SUCCESS`, current_version:
|
||||
新版本号)。
|
||||
2. **回滚场景**: 设备回滚到旧版本后,向平台发送 `UpdateStatusReport` (status: `FAILED`, error_code: `ERR_ROLLED_BACK`,
|
||||
error_message: "新固件启动失败,已自动回滚", current_version: 旧版本号)。
|
||||
3. **重复发送**: 为了提高在单向 LoRa 通信中的可靠性,设备在发送最终状态报告时,应在短时间内重复发送多次(例如 3-5 次)。
|
||||
|
||||
## 6. 关键技术点与注意事项
|
||||
|
||||
### 6.1. LoRa 传输层
|
||||
|
||||
* 确保 `internal/infra/transport/lora/lora_mesh_uart_passthrough_transport.go` 能稳定处理大尺寸 Protobuf 消息的分片和重组。
|
||||
|
||||
### 6.2. 平台侧的请求处理
|
||||
|
||||
* `internal/app/listener/lora_listener.go` 在接收 `RequestFile` 消息时,需要高效处理,避免阻塞监听器。
|
||||
|
||||
### 6.3. 文件系统操作 (MicroPython)
|
||||
|
||||
* 确保文件系统操作(创建目录、写入文件、删除文件)的正确性和鲁棒性,并对错误进行捕获和报告。
|
||||
|
||||
### 6.4. MD5 校验 (MicroPython)
|
||||
|
||||
* MicroPython 的 `hashlib` 模块提供 MD5 算法。确保计算的效率和准确性。
|
||||
|
||||
### 6.5. OTA 分区管理 (MicroPython)
|
||||
|
||||
* 熟悉 ESP-IDF 的 OTA 机制在 MicroPython 中的绑定和使用方法。正确调用 API 来设置启动分区和标记应用有效。
|
||||
|
||||
### 6.6. 回滚机制
|
||||
|
||||
* 完全依赖 ESP-IDF 提供的 OTA 回滚机制。新固件必须在启动后标记自身为有效,否则会自动回滚。
|
||||
|
||||
### 6.7. 错误处理与重试
|
||||
|
||||
* **设备侧**: 实现文件级别的下载和校验重试。对于无法恢复的错误,立即上报 `FAILED` 状态并中止任务。
|
||||
* **平台侧**: 实现任务级别的总超时管理。这是处理设备意外断电、失联等情况的关键机制。设备重启后无需保留升级状态,简化了设备端逻辑。
|
||||
|
||||
### 6.8. 安全性
|
||||
|
||||
* **数字签名**: 强烈建议尽快实现清单文件的数字签名。**没有数字签名,OTA 过程将面临严重的安全风险(如中间人攻击)**
|
||||
,攻击者可能下发恶意固件。平台的公钥需要被硬编码到设备固件中,作为信任的根基。
|
||||
* **LoRaWAN 安全**: 确保 LoRaWAN 的网络层和应用层密钥管理得当, 防止未经授权的设备加入网络或窃听数据。
|
||||
|
||||
---
|
||||
|
||||
## 7. 固件 OTA 升级流程描述
|
||||
|
||||
### 阶段一:任务准备与下发
|
||||
|
||||
1. **上传与准备 (Developer -> Platform)**: 开发者上传固件包,平台解压、计算MD5、生成清单文件、创建升级任务。
|
||||
2. **下发更新通知 (Platform -> Device)**: 平台向设备发送 `PrepareUpdateReq`。
|
||||
|
||||
### 阶段二:设备版本检查与准备
|
||||
|
||||
1. **版本检查 (Device)**:
|
||||
* **失败分支 (降级/同版本)**: 设备拒绝升级,上报 `FAILED` (ERR_VERSION_ROLLBACK) 或 `SUCCESS` (
|
||||
SUCCESS_ALREADY_UP_TO_DATE),流程结束。
|
||||
* **成功分支**: 版本检查通过,设备继续。
|
||||
2. **设备准备 (Device)**:
|
||||
* 设备清空非活动分区。
|
||||
* **失败分支**: 上报 `FAILED` (ERR_PREPARE),流程结束。
|
||||
* **成功分支**: 设备发送 `RequestFile` 请求清单文件。
|
||||
|
||||
### 阶段三:文件循环下载和校验
|
||||
|
||||
1. **清单文件传输与校验 (Platform <-> Device)**:
|
||||
* 平台发送清单文件,设备接收并校验。
|
||||
* **失败分支**: 上报 `FAILED` (ERR_MANIFEST_VERIFY),流程结束。
|
||||
2. **固件文件循环 (Device <-> Platform)**:
|
||||
* 设备逐个请求、下载、校验清单中的所有文件。
|
||||
* **失败分支 (重试耗尽)**: 上报 `FAILED` (ERR_DOWNLOAD / ERR_VERIFY),流程结束。
|
||||
|
||||
### 阶段四:激活与最终状态
|
||||
|
||||
1. **激活重启 (Device)**: 所有文件成功下载后,设备配置启动分区并重启。
|
||||
2. **新固件自检 (Device)**:
|
||||
* **成功分支**:
|
||||
* 设备标记自身为有效。
|
||||
* 设备上报 `SUCCESS`。
|
||||
* 平台更新任务状态为 `SUCCESS`。
|
||||
* **失败分支 (自检失败/未标记)**:
|
||||
* 设备等待底层机制自动回滚。
|
||||
* 设备回滚后,上报 `FAILED` (ERR_ROLLED_BACK)。
|
||||
* 平台更新任务状态为 `FAILED`。
|
||||
3. **总超时检查 (Platform)**: 如果在规定时间内未收到任何最终报告,平台将任务标记为 `FAILED` (ERR_TIMEOUT)。
|
||||
10824
docs/docs.go
Normal file
10824
docs/docs.go
Normal file
File diff suppressed because it is too large
Load Diff
10798
docs/swagger.json
Normal file
10798
docs/swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
6873
docs/swagger.yaml
Normal file
6873
docs/swagger.yaml
Normal file
File diff suppressed because it is too large
Load Diff
1
frontend/dist/assets/index.bcc76856.css
vendored
1
frontend/dist/assets/index.bcc76856.css
vendored
File diff suppressed because one or more lines are too long
21
frontend/dist/assets/index.cb9d3828.js
vendored
21
frontend/dist/assets/index.cb9d3828.js
vendored
File diff suppressed because one or more lines are too long
16
frontend/dist/index.html
vendored
16
frontend/dist/index.html
vendored
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>猪场管理系统</title>
|
||||
<script type="module" crossorigin src="/assets/index.cb9d3828.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.bcc76856.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- Vue应用将挂载到这个div上 -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>猪场管理系统</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- Vue应用将挂载到这个div上 -->
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
12
frontend/node_modules/.bin/esbuild
generated
vendored
12
frontend/node_modules/.bin/esbuild
generated
vendored
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../esbuild/bin/esbuild" "$@"
|
||||
else
|
||||
exec node "$basedir/../esbuild/bin/esbuild" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/esbuild.cmd
generated
vendored
17
frontend/node_modules/.bin/esbuild.cmd
generated
vendored
@@ -1,17 +0,0 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esbuild\bin\esbuild" %*
|
||||
28
frontend/node_modules/.bin/esbuild.ps1
generated
vendored
28
frontend/node_modules/.bin/esbuild.ps1
generated
vendored
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../esbuild/bin/esbuild" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
12
frontend/node_modules/.bin/nanoid
generated
vendored
12
frontend/node_modules/.bin/nanoid
generated
vendored
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../nanoid/bin/nanoid.cjs" "$@"
|
||||
else
|
||||
exec node "$basedir/../nanoid/bin/nanoid.cjs" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/nanoid.cmd
generated
vendored
17
frontend/node_modules/.bin/nanoid.cmd
generated
vendored
@@ -1,17 +0,0 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %*
|
||||
28
frontend/node_modules/.bin/nanoid.ps1
generated
vendored
28
frontend/node_modules/.bin/nanoid.ps1
generated
vendored
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
12
frontend/node_modules/.bin/parser
generated
vendored
12
frontend/node_modules/.bin/parser
generated
vendored
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/parser.cmd
generated
vendored
17
frontend/node_modules/.bin/parser.cmd
generated
vendored
@@ -1,17 +0,0 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\@babel\parser\bin\babel-parser.js" %*
|
||||
28
frontend/node_modules/.bin/parser.ps1
generated
vendored
28
frontend/node_modules/.bin/parser.ps1
generated
vendored
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../@babel/parser/bin/babel-parser.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
12
frontend/node_modules/.bin/resolve
generated
vendored
12
frontend/node_modules/.bin/resolve
generated
vendored
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../resolve/bin/resolve" "$@"
|
||||
else
|
||||
exec node "$basedir/../resolve/bin/resolve" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/resolve.cmd
generated
vendored
17
frontend/node_modules/.bin/resolve.cmd
generated
vendored
@@ -1,17 +0,0 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\resolve\bin\resolve" %*
|
||||
28
frontend/node_modules/.bin/resolve.ps1
generated
vendored
28
frontend/node_modules/.bin/resolve.ps1
generated
vendored
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../resolve/bin/resolve" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../resolve/bin/resolve" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../resolve/bin/resolve" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../resolve/bin/resolve" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
12
frontend/node_modules/.bin/rollup
generated
vendored
12
frontend/node_modules/.bin/rollup
generated
vendored
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../rollup/dist/bin/rollup" "$@"
|
||||
else
|
||||
exec node "$basedir/../rollup/dist/bin/rollup" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/rollup.cmd
generated
vendored
17
frontend/node_modules/.bin/rollup.cmd
generated
vendored
@@ -1,17 +0,0 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rollup\dist\bin\rollup" %*
|
||||
28
frontend/node_modules/.bin/rollup.ps1
generated
vendored
28
frontend/node_modules/.bin/rollup.ps1
generated
vendored
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
12
frontend/node_modules/.bin/vite
generated
vendored
12
frontend/node_modules/.bin/vite
generated
vendored
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../vite/bin/vite.js" "$@"
|
||||
else
|
||||
exec node "$basedir/../vite/bin/vite.js" "$@"
|
||||
fi
|
||||
17
frontend/node_modules/.bin/vite.cmd
generated
vendored
17
frontend/node_modules/.bin/vite.cmd
generated
vendored
@@ -1,17 +0,0 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\vite\bin\vite.js" %*
|
||||
28
frontend/node_modules/.bin/vite.ps1
generated
vendored
28
frontend/node_modules/.bin/vite.ps1
generated
vendored
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../vite/bin/vite.js" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
475
frontend/node_modules/.package-lock.json
generated
vendored
475
frontend/node_modules/.package-lock.json
generated
vendored
@@ -1,475 +0,0 @@
|
||||
{
|
||||
"name": "pig-farm-controller-frontend",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
|
||||
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.4.tgz",
|
||||
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.4"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.4.tgz",
|
||||
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.27.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz",
|
||||
"integrity": "sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^3.0.0",
|
||||
"vue": "^3.2.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
|
||||
"integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.28.3",
|
||||
"@vue/shared": "3.5.21",
|
||||
"entities": "^4.5.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
|
||||
"integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.5.21",
|
||||
"@vue/shared": "3.5.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
|
||||
"integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.28.3",
|
||||
"@vue/compiler-core": "3.5.21",
|
||||
"@vue/compiler-dom": "3.5.21",
|
||||
"@vue/compiler-ssr": "3.5.21",
|
||||
"@vue/shared": "3.5.21",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.18",
|
||||
"postcss": "^8.5.6",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
|
||||
"integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.21",
|
||||
"@vue/shared": "3.5.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.21.tgz",
|
||||
"integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
|
||||
"integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.21",
|
||||
"@vue/shared": "3.5.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
|
||||
"integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.21",
|
||||
"@vue/runtime-core": "3.5.21",
|
||||
"@vue/shared": "3.5.21",
|
||||
"csstype": "^3.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
|
||||
"integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.5.21",
|
||||
"@vue/shared": "3.5.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.5.21"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.21.tgz",
|
||||
"integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw=="
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.15.18",
|
||||
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.15.18.tgz",
|
||||
"integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/android-arm": "0.15.18",
|
||||
"@esbuild/linux-loong64": "0.15.18",
|
||||
"esbuild-android-64": "0.15.18",
|
||||
"esbuild-android-arm64": "0.15.18",
|
||||
"esbuild-darwin-64": "0.15.18",
|
||||
"esbuild-darwin-arm64": "0.15.18",
|
||||
"esbuild-freebsd-64": "0.15.18",
|
||||
"esbuild-freebsd-arm64": "0.15.18",
|
||||
"esbuild-linux-32": "0.15.18",
|
||||
"esbuild-linux-64": "0.15.18",
|
||||
"esbuild-linux-arm": "0.15.18",
|
||||
"esbuild-linux-arm64": "0.15.18",
|
||||
"esbuild-linux-mips64le": "0.15.18",
|
||||
"esbuild-linux-ppc64le": "0.15.18",
|
||||
"esbuild-linux-riscv64": "0.15.18",
|
||||
"esbuild-linux-s390x": "0.15.18",
|
||||
"esbuild-netbsd-64": "0.15.18",
|
||||
"esbuild-openbsd-64": "0.15.18",
|
||||
"esbuild-sunos-64": "0.15.18",
|
||||
"esbuild-windows-32": "0.15.18",
|
||||
"esbuild-windows-64": "0.15.18",
|
||||
"esbuild-windows-arm64": "0.15.18"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-windows-64": {
|
||||
"version": "0.15.18",
|
||||
"resolved": "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz",
|
||||
"integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.18",
|
||||
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.18.tgz",
|
||||
"integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "2.79.2",
|
||||
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-2.79.2.tgz",
|
||||
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "3.2.11",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-3.2.11.tgz",
|
||||
"integrity": "sha512-K/jGKL/PgbIgKCiJo5QbASQhFiV02X9Jh+Qq0AKCRCRKZtOTVi4t6wh75FDpGf2N9rYOnzH87OEFQNaFy6pdxQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.15.9",
|
||||
"postcss": "^8.4.18",
|
||||
"resolve": "^1.22.1",
|
||||
"rollup": "^2.79.1"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">= 14",
|
||||
"less": "*",
|
||||
"sass": "*",
|
||||
"stylus": "*",
|
||||
"sugarss": "*",
|
||||
"terser": "^5.4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"less": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"stylus": {
|
||||
"optional": true
|
||||
},
|
||||
"sugarss": {
|
||||
"optional": true
|
||||
},
|
||||
"terser": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.5.21",
|
||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.21.tgz",
|
||||
"integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.21",
|
||||
"@vue/compiler-sfc": "3.5.21",
|
||||
"@vue/runtime-dom": "3.5.21",
|
||||
"@vue/server-renderer": "3.5.21",
|
||||
"@vue/shared": "3.5.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz",
|
||||
"integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.6.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
frontend/node_modules/.vite/deps_temp/_metadata.json
generated
vendored
23
frontend/node_modules/.vite/deps_temp/_metadata.json
generated
vendored
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"hash": "dd8a32db",
|
||||
"browserHash": "91f1c2e1",
|
||||
"optimized": {
|
||||
"vue": {
|
||||
"src": "../../vue/dist/vue.runtime.esm-bundler.js",
|
||||
"file": "vue.js",
|
||||
"fileHash": "6d85718e",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vue-router": {
|
||||
"src": "../../vue-router/dist/vue-router.mjs",
|
||||
"file": "vue-router.js",
|
||||
"fileHash": "1e430545",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-DB3RJHEA": {
|
||||
"file": "chunk-DB3RJHEA.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
12441
frontend/node_modules/.vite/deps_temp/chunk-DB3RJHEA.js
generated
vendored
12441
frontend/node_modules/.vite/deps_temp/chunk-DB3RJHEA.js
generated
vendored
File diff suppressed because it is too large
Load Diff
7
frontend/node_modules/.vite/deps_temp/chunk-DB3RJHEA.js.map
generated
vendored
7
frontend/node_modules/.vite/deps_temp/chunk-DB3RJHEA.js.map
generated
vendored
File diff suppressed because one or more lines are too long
1
frontend/node_modules/.vite/deps_temp/package.json
generated
vendored
1
frontend/node_modules/.vite/deps_temp/package.json
generated
vendored
@@ -1 +0,0 @@
|
||||
{"type":"module"}
|
||||
2768
frontend/node_modules/.vite/deps_temp/vue-router.js
generated
vendored
2768
frontend/node_modules/.vite/deps_temp/vue-router.js
generated
vendored
File diff suppressed because it is too large
Load Diff
7
frontend/node_modules/.vite/deps_temp/vue-router.js.map
generated
vendored
7
frontend/node_modules/.vite/deps_temp/vue-router.js.map
generated
vendored
File diff suppressed because one or more lines are too long
343
frontend/node_modules/.vite/deps_temp/vue.js
generated
vendored
343
frontend/node_modules/.vite/deps_temp/vue.js
generated
vendored
@@ -1,343 +0,0 @@
|
||||
import {
|
||||
BaseTransition,
|
||||
BaseTransitionPropsValidators,
|
||||
Comment,
|
||||
DeprecationTypes,
|
||||
EffectScope,
|
||||
ErrorCodes,
|
||||
ErrorTypeStrings,
|
||||
Fragment,
|
||||
KeepAlive,
|
||||
ReactiveEffect,
|
||||
Static,
|
||||
Suspense,
|
||||
Teleport,
|
||||
Text,
|
||||
TrackOpTypes,
|
||||
Transition,
|
||||
TransitionGroup,
|
||||
TriggerOpTypes,
|
||||
VueElement,
|
||||
assertNumber,
|
||||
callWithAsyncErrorHandling,
|
||||
callWithErrorHandling,
|
||||
camelize,
|
||||
capitalize,
|
||||
cloneVNode,
|
||||
compatUtils,
|
||||
compile,
|
||||
computed,
|
||||
createApp,
|
||||
createBaseVNode,
|
||||
createBlock,
|
||||
createCommentVNode,
|
||||
createElementBlock,
|
||||
createHydrationRenderer,
|
||||
createPropsRestProxy,
|
||||
createRenderer,
|
||||
createSSRApp,
|
||||
createSlots,
|
||||
createStaticVNode,
|
||||
createTextVNode,
|
||||
createVNode,
|
||||
customRef,
|
||||
defineAsyncComponent,
|
||||
defineComponent,
|
||||
defineCustomElement,
|
||||
defineEmits,
|
||||
defineExpose,
|
||||
defineModel,
|
||||
defineOptions,
|
||||
defineProps,
|
||||
defineSSRCustomElement,
|
||||
defineSlots,
|
||||
devtools,
|
||||
effect,
|
||||
effectScope,
|
||||
getCurrentInstance,
|
||||
getCurrentScope,
|
||||
getCurrentWatcher,
|
||||
getTransitionRawChildren,
|
||||
guardReactiveProps,
|
||||
h,
|
||||
handleError,
|
||||
hasInjectionContext,
|
||||
hydrate,
|
||||
hydrateOnIdle,
|
||||
hydrateOnInteraction,
|
||||
hydrateOnMediaQuery,
|
||||
hydrateOnVisible,
|
||||
initCustomFormatter,
|
||||
initDirectivesForSSR,
|
||||
inject,
|
||||
isMemoSame,
|
||||
isProxy,
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isRef,
|
||||
isRuntimeOnly,
|
||||
isShallow,
|
||||
isVNode,
|
||||
markRaw,
|
||||
mergeDefaults,
|
||||
mergeModels,
|
||||
mergeProps,
|
||||
nextTick,
|
||||
normalizeClass,
|
||||
normalizeProps,
|
||||
normalizeStyle,
|
||||
onActivated,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onBeforeUpdate,
|
||||
onDeactivated,
|
||||
onErrorCaptured,
|
||||
onMounted,
|
||||
onRenderTracked,
|
||||
onRenderTriggered,
|
||||
onScopeDispose,
|
||||
onServerPrefetch,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
onWatcherCleanup,
|
||||
openBlock,
|
||||
popScopeId,
|
||||
provide,
|
||||
proxyRefs,
|
||||
pushScopeId,
|
||||
queuePostFlushCb,
|
||||
reactive,
|
||||
readonly,
|
||||
ref,
|
||||
registerRuntimeCompiler,
|
||||
render,
|
||||
renderList,
|
||||
renderSlot,
|
||||
resolveComponent,
|
||||
resolveDirective,
|
||||
resolveDynamicComponent,
|
||||
resolveFilter,
|
||||
resolveTransitionHooks,
|
||||
setBlockTracking,
|
||||
setDevtoolsHook,
|
||||
setTransitionHooks,
|
||||
shallowReactive,
|
||||
shallowReadonly,
|
||||
shallowRef,
|
||||
ssrContextKey,
|
||||
ssrUtils,
|
||||
stop,
|
||||
toDisplayString,
|
||||
toHandlerKey,
|
||||
toHandlers,
|
||||
toRaw,
|
||||
toRef,
|
||||
toRefs,
|
||||
toValue,
|
||||
transformVNodeArgs,
|
||||
triggerRef,
|
||||
unref,
|
||||
useAttrs,
|
||||
useCssModule,
|
||||
useCssVars,
|
||||
useHost,
|
||||
useId,
|
||||
useModel,
|
||||
useSSRContext,
|
||||
useShadowRoot,
|
||||
useSlots,
|
||||
useTemplateRef,
|
||||
useTransitionState,
|
||||
vModelCheckbox,
|
||||
vModelDynamic,
|
||||
vModelRadio,
|
||||
vModelSelect,
|
||||
vModelText,
|
||||
vShow,
|
||||
version,
|
||||
warn,
|
||||
watch,
|
||||
watchEffect,
|
||||
watchPostEffect,
|
||||
watchSyncEffect,
|
||||
withAsyncContext,
|
||||
withCtx,
|
||||
withDefaults,
|
||||
withDirectives,
|
||||
withKeys,
|
||||
withMemo,
|
||||
withModifiers,
|
||||
withScopeId
|
||||
} from "./chunk-DB3RJHEA.js";
|
||||
export {
|
||||
BaseTransition,
|
||||
BaseTransitionPropsValidators,
|
||||
Comment,
|
||||
DeprecationTypes,
|
||||
EffectScope,
|
||||
ErrorCodes,
|
||||
ErrorTypeStrings,
|
||||
Fragment,
|
||||
KeepAlive,
|
||||
ReactiveEffect,
|
||||
Static,
|
||||
Suspense,
|
||||
Teleport,
|
||||
Text,
|
||||
TrackOpTypes,
|
||||
Transition,
|
||||
TransitionGroup,
|
||||
TriggerOpTypes,
|
||||
VueElement,
|
||||
assertNumber,
|
||||
callWithAsyncErrorHandling,
|
||||
callWithErrorHandling,
|
||||
camelize,
|
||||
capitalize,
|
||||
cloneVNode,
|
||||
compatUtils,
|
||||
compile,
|
||||
computed,
|
||||
createApp,
|
||||
createBlock,
|
||||
createCommentVNode,
|
||||
createElementBlock,
|
||||
createBaseVNode as createElementVNode,
|
||||
createHydrationRenderer,
|
||||
createPropsRestProxy,
|
||||
createRenderer,
|
||||
createSSRApp,
|
||||
createSlots,
|
||||
createStaticVNode,
|
||||
createTextVNode,
|
||||
createVNode,
|
||||
customRef,
|
||||
defineAsyncComponent,
|
||||
defineComponent,
|
||||
defineCustomElement,
|
||||
defineEmits,
|
||||
defineExpose,
|
||||
defineModel,
|
||||
defineOptions,
|
||||
defineProps,
|
||||
defineSSRCustomElement,
|
||||
defineSlots,
|
||||
devtools,
|
||||
effect,
|
||||
effectScope,
|
||||
getCurrentInstance,
|
||||
getCurrentScope,
|
||||
getCurrentWatcher,
|
||||
getTransitionRawChildren,
|
||||
guardReactiveProps,
|
||||
h,
|
||||
handleError,
|
||||
hasInjectionContext,
|
||||
hydrate,
|
||||
hydrateOnIdle,
|
||||
hydrateOnInteraction,
|
||||
hydrateOnMediaQuery,
|
||||
hydrateOnVisible,
|
||||
initCustomFormatter,
|
||||
initDirectivesForSSR,
|
||||
inject,
|
||||
isMemoSame,
|
||||
isProxy,
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isRef,
|
||||
isRuntimeOnly,
|
||||
isShallow,
|
||||
isVNode,
|
||||
markRaw,
|
||||
mergeDefaults,
|
||||
mergeModels,
|
||||
mergeProps,
|
||||
nextTick,
|
||||
normalizeClass,
|
||||
normalizeProps,
|
||||
normalizeStyle,
|
||||
onActivated,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onBeforeUpdate,
|
||||
onDeactivated,
|
||||
onErrorCaptured,
|
||||
onMounted,
|
||||
onRenderTracked,
|
||||
onRenderTriggered,
|
||||
onScopeDispose,
|
||||
onServerPrefetch,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
onWatcherCleanup,
|
||||
openBlock,
|
||||
popScopeId,
|
||||
provide,
|
||||
proxyRefs,
|
||||
pushScopeId,
|
||||
queuePostFlushCb,
|
||||
reactive,
|
||||
readonly,
|
||||
ref,
|
||||
registerRuntimeCompiler,
|
||||
render,
|
||||
renderList,
|
||||
renderSlot,
|
||||
resolveComponent,
|
||||
resolveDirective,
|
||||
resolveDynamicComponent,
|
||||
resolveFilter,
|
||||
resolveTransitionHooks,
|
||||
setBlockTracking,
|
||||
setDevtoolsHook,
|
||||
setTransitionHooks,
|
||||
shallowReactive,
|
||||
shallowReadonly,
|
||||
shallowRef,
|
||||
ssrContextKey,
|
||||
ssrUtils,
|
||||
stop,
|
||||
toDisplayString,
|
||||
toHandlerKey,
|
||||
toHandlers,
|
||||
toRaw,
|
||||
toRef,
|
||||
toRefs,
|
||||
toValue,
|
||||
transformVNodeArgs,
|
||||
triggerRef,
|
||||
unref,
|
||||
useAttrs,
|
||||
useCssModule,
|
||||
useCssVars,
|
||||
useHost,
|
||||
useId,
|
||||
useModel,
|
||||
useSSRContext,
|
||||
useShadowRoot,
|
||||
useSlots,
|
||||
useTemplateRef,
|
||||
useTransitionState,
|
||||
vModelCheckbox,
|
||||
vModelDynamic,
|
||||
vModelRadio,
|
||||
vModelSelect,
|
||||
vModelText,
|
||||
vShow,
|
||||
version,
|
||||
warn,
|
||||
watch,
|
||||
watchEffect,
|
||||
watchPostEffect,
|
||||
watchSyncEffect,
|
||||
withAsyncContext,
|
||||
withCtx,
|
||||
withDefaults,
|
||||
withDirectives,
|
||||
withKeys,
|
||||
withMemo,
|
||||
withModifiers,
|
||||
withScopeId
|
||||
};
|
||||
//# sourceMappingURL=vue.js.map
|
||||
7
frontend/node_modules/.vite/deps_temp/vue.js.map
generated
vendored
7
frontend/node_modules/.vite/deps_temp/vue.js.map
generated
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
22
frontend/node_modules/@babel/helper-string-parser/LICENSE
generated
vendored
22
frontend/node_modules/@babel/helper-string-parser/LICENSE
generated
vendored
@@ -1,22 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
frontend/node_modules/@babel/helper-string-parser/README.md
generated
vendored
19
frontend/node_modules/@babel/helper-string-parser/README.md
generated
vendored
@@ -1,19 +0,0 @@
|
||||
# @babel/helper-string-parser
|
||||
|
||||
> A utility package to parse strings
|
||||
|
||||
See our website [@babel/helper-string-parser](https://babeljs.io/docs/babel-helper-string-parser) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save @babel/helper-string-parser
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/helper-string-parser
|
||||
```
|
||||
295
frontend/node_modules/@babel/helper-string-parser/lib/index.js
generated
vendored
295
frontend/node_modules/@babel/helper-string-parser/lib/index.js
generated
vendored
@@ -1,295 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.readCodePoint = readCodePoint;
|
||||
exports.readInt = readInt;
|
||||
exports.readStringContents = readStringContents;
|
||||
var _isDigit = function isDigit(code) {
|
||||
return code >= 48 && code <= 57;
|
||||
};
|
||||
const forbiddenNumericSeparatorSiblings = {
|
||||
decBinOct: new Set([46, 66, 69, 79, 95, 98, 101, 111]),
|
||||
hex: new Set([46, 88, 95, 120])
|
||||
};
|
||||
const isAllowedNumericSeparatorSibling = {
|
||||
bin: ch => ch === 48 || ch === 49,
|
||||
oct: ch => ch >= 48 && ch <= 55,
|
||||
dec: ch => ch >= 48 && ch <= 57,
|
||||
hex: ch => ch >= 48 && ch <= 57 || ch >= 65 && ch <= 70 || ch >= 97 && ch <= 102
|
||||
};
|
||||
function readStringContents(type, input, pos, lineStart, curLine, errors) {
|
||||
const initialPos = pos;
|
||||
const initialLineStart = lineStart;
|
||||
const initialCurLine = curLine;
|
||||
let out = "";
|
||||
let firstInvalidLoc = null;
|
||||
let chunkStart = pos;
|
||||
const {
|
||||
length
|
||||
} = input;
|
||||
for (;;) {
|
||||
if (pos >= length) {
|
||||
errors.unterminated(initialPos, initialLineStart, initialCurLine);
|
||||
out += input.slice(chunkStart, pos);
|
||||
break;
|
||||
}
|
||||
const ch = input.charCodeAt(pos);
|
||||
if (isStringEnd(type, ch, input, pos)) {
|
||||
out += input.slice(chunkStart, pos);
|
||||
break;
|
||||
}
|
||||
if (ch === 92) {
|
||||
out += input.slice(chunkStart, pos);
|
||||
const res = readEscapedChar(input, pos, lineStart, curLine, type === "template", errors);
|
||||
if (res.ch === null && !firstInvalidLoc) {
|
||||
firstInvalidLoc = {
|
||||
pos,
|
||||
lineStart,
|
||||
curLine
|
||||
};
|
||||
} else {
|
||||
out += res.ch;
|
||||
}
|
||||
({
|
||||
pos,
|
||||
lineStart,
|
||||
curLine
|
||||
} = res);
|
||||
chunkStart = pos;
|
||||
} else if (ch === 8232 || ch === 8233) {
|
||||
++pos;
|
||||
++curLine;
|
||||
lineStart = pos;
|
||||
} else if (ch === 10 || ch === 13) {
|
||||
if (type === "template") {
|
||||
out += input.slice(chunkStart, pos) + "\n";
|
||||
++pos;
|
||||
if (ch === 13 && input.charCodeAt(pos) === 10) {
|
||||
++pos;
|
||||
}
|
||||
++curLine;
|
||||
chunkStart = lineStart = pos;
|
||||
} else {
|
||||
errors.unterminated(initialPos, initialLineStart, initialCurLine);
|
||||
}
|
||||
} else {
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
return {
|
||||
pos,
|
||||
str: out,
|
||||
firstInvalidLoc,
|
||||
lineStart,
|
||||
curLine,
|
||||
containsInvalid: !!firstInvalidLoc
|
||||
};
|
||||
}
|
||||
function isStringEnd(type, ch, input, pos) {
|
||||
if (type === "template") {
|
||||
return ch === 96 || ch === 36 && input.charCodeAt(pos + 1) === 123;
|
||||
}
|
||||
return ch === (type === "double" ? 34 : 39);
|
||||
}
|
||||
function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) {
|
||||
const throwOnInvalid = !inTemplate;
|
||||
pos++;
|
||||
const res = ch => ({
|
||||
pos,
|
||||
ch,
|
||||
lineStart,
|
||||
curLine
|
||||
});
|
||||
const ch = input.charCodeAt(pos++);
|
||||
switch (ch) {
|
||||
case 110:
|
||||
return res("\n");
|
||||
case 114:
|
||||
return res("\r");
|
||||
case 120:
|
||||
{
|
||||
let code;
|
||||
({
|
||||
code,
|
||||
pos
|
||||
} = readHexChar(input, pos, lineStart, curLine, 2, false, throwOnInvalid, errors));
|
||||
return res(code === null ? null : String.fromCharCode(code));
|
||||
}
|
||||
case 117:
|
||||
{
|
||||
let code;
|
||||
({
|
||||
code,
|
||||
pos
|
||||
} = readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors));
|
||||
return res(code === null ? null : String.fromCodePoint(code));
|
||||
}
|
||||
case 116:
|
||||
return res("\t");
|
||||
case 98:
|
||||
return res("\b");
|
||||
case 118:
|
||||
return res("\u000b");
|
||||
case 102:
|
||||
return res("\f");
|
||||
case 13:
|
||||
if (input.charCodeAt(pos) === 10) {
|
||||
++pos;
|
||||
}
|
||||
case 10:
|
||||
lineStart = pos;
|
||||
++curLine;
|
||||
case 8232:
|
||||
case 8233:
|
||||
return res("");
|
||||
case 56:
|
||||
case 57:
|
||||
if (inTemplate) {
|
||||
return res(null);
|
||||
} else {
|
||||
errors.strictNumericEscape(pos - 1, lineStart, curLine);
|
||||
}
|
||||
default:
|
||||
if (ch >= 48 && ch <= 55) {
|
||||
const startPos = pos - 1;
|
||||
const match = /^[0-7]+/.exec(input.slice(startPos, pos + 2));
|
||||
let octalStr = match[0];
|
||||
let octal = parseInt(octalStr, 8);
|
||||
if (octal > 255) {
|
||||
octalStr = octalStr.slice(0, -1);
|
||||
octal = parseInt(octalStr, 8);
|
||||
}
|
||||
pos += octalStr.length - 1;
|
||||
const next = input.charCodeAt(pos);
|
||||
if (octalStr !== "0" || next === 56 || next === 57) {
|
||||
if (inTemplate) {
|
||||
return res(null);
|
||||
} else {
|
||||
errors.strictNumericEscape(startPos, lineStart, curLine);
|
||||
}
|
||||
}
|
||||
return res(String.fromCharCode(octal));
|
||||
}
|
||||
return res(String.fromCharCode(ch));
|
||||
}
|
||||
}
|
||||
function readHexChar(input, pos, lineStart, curLine, len, forceLen, throwOnInvalid, errors) {
|
||||
const initialPos = pos;
|
||||
let n;
|
||||
({
|
||||
n,
|
||||
pos
|
||||
} = readInt(input, pos, lineStart, curLine, 16, len, forceLen, false, errors, !throwOnInvalid));
|
||||
if (n === null) {
|
||||
if (throwOnInvalid) {
|
||||
errors.invalidEscapeSequence(initialPos, lineStart, curLine);
|
||||
} else {
|
||||
pos = initialPos - 1;
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: n,
|
||||
pos
|
||||
};
|
||||
}
|
||||
function readInt(input, pos, lineStart, curLine, radix, len, forceLen, allowNumSeparator, errors, bailOnError) {
|
||||
const start = pos;
|
||||
const forbiddenSiblings = radix === 16 ? forbiddenNumericSeparatorSiblings.hex : forbiddenNumericSeparatorSiblings.decBinOct;
|
||||
const isAllowedSibling = radix === 16 ? isAllowedNumericSeparatorSibling.hex : radix === 10 ? isAllowedNumericSeparatorSibling.dec : radix === 8 ? isAllowedNumericSeparatorSibling.oct : isAllowedNumericSeparatorSibling.bin;
|
||||
let invalid = false;
|
||||
let total = 0;
|
||||
for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
|
||||
const code = input.charCodeAt(pos);
|
||||
let val;
|
||||
if (code === 95 && allowNumSeparator !== "bail") {
|
||||
const prev = input.charCodeAt(pos - 1);
|
||||
const next = input.charCodeAt(pos + 1);
|
||||
if (!allowNumSeparator) {
|
||||
if (bailOnError) return {
|
||||
n: null,
|
||||
pos
|
||||
};
|
||||
errors.numericSeparatorInEscapeSequence(pos, lineStart, curLine);
|
||||
} else if (Number.isNaN(next) || !isAllowedSibling(next) || forbiddenSiblings.has(prev) || forbiddenSiblings.has(next)) {
|
||||
if (bailOnError) return {
|
||||
n: null,
|
||||
pos
|
||||
};
|
||||
errors.unexpectedNumericSeparator(pos, lineStart, curLine);
|
||||
}
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
if (code >= 97) {
|
||||
val = code - 97 + 10;
|
||||
} else if (code >= 65) {
|
||||
val = code - 65 + 10;
|
||||
} else if (_isDigit(code)) {
|
||||
val = code - 48;
|
||||
} else {
|
||||
val = Infinity;
|
||||
}
|
||||
if (val >= radix) {
|
||||
if (val <= 9 && bailOnError) {
|
||||
return {
|
||||
n: null,
|
||||
pos
|
||||
};
|
||||
} else if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) {
|
||||
val = 0;
|
||||
} else if (forceLen) {
|
||||
val = 0;
|
||||
invalid = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++pos;
|
||||
total = total * radix + val;
|
||||
}
|
||||
if (pos === start || len != null && pos - start !== len || invalid) {
|
||||
return {
|
||||
n: null,
|
||||
pos
|
||||
};
|
||||
}
|
||||
return {
|
||||
n: total,
|
||||
pos
|
||||
};
|
||||
}
|
||||
function readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors) {
|
||||
const ch = input.charCodeAt(pos);
|
||||
let code;
|
||||
if (ch === 123) {
|
||||
++pos;
|
||||
({
|
||||
code,
|
||||
pos
|
||||
} = readHexChar(input, pos, lineStart, curLine, input.indexOf("}", pos) - pos, true, throwOnInvalid, errors));
|
||||
++pos;
|
||||
if (code !== null && code > 0x10ffff) {
|
||||
if (throwOnInvalid) {
|
||||
errors.invalidCodePoint(pos, lineStart, curLine);
|
||||
} else {
|
||||
return {
|
||||
code: null,
|
||||
pos
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
({
|
||||
code,
|
||||
pos
|
||||
} = readHexChar(input, pos, lineStart, curLine, 4, false, throwOnInvalid, errors));
|
||||
}
|
||||
return {
|
||||
code,
|
||||
pos
|
||||
};
|
||||
}
|
||||
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
frontend/node_modules/@babel/helper-string-parser/lib/index.js.map
generated
vendored
1
frontend/node_modules/@babel/helper-string-parser/lib/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
31
frontend/node_modules/@babel/helper-string-parser/package.json
generated
vendored
31
frontend/node_modules/@babel/helper-string-parser/package.json
generated
vendored
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "@babel/helper-string-parser",
|
||||
"version": "7.27.1",
|
||||
"description": "A utility package to parse strings",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-helper-string-parser"
|
||||
},
|
||||
"homepage": "https://babel.dev/docs/en/next/babel-helper-string-parser",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"devDependencies": {
|
||||
"charcodes": "^0.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"type": "commonjs"
|
||||
}
|
||||
22
frontend/node_modules/@babel/helper-validator-identifier/LICENSE
generated
vendored
22
frontend/node_modules/@babel/helper-validator-identifier/LICENSE
generated
vendored
@@ -1,22 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
frontend/node_modules/@babel/helper-validator-identifier/README.md
generated
vendored
19
frontend/node_modules/@babel/helper-validator-identifier/README.md
generated
vendored
@@ -1,19 +0,0 @@
|
||||
# @babel/helper-validator-identifier
|
||||
|
||||
> Validate identifier/keywords name
|
||||
|
||||
See our website [@babel/helper-validator-identifier](https://babeljs.io/docs/babel-helper-validator-identifier) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save @babel/helper-validator-identifier
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/helper-validator-identifier
|
||||
```
|
||||
70
frontend/node_modules/@babel/helper-validator-identifier/lib/identifier.js
generated
vendored
70
frontend/node_modules/@babel/helper-validator-identifier/lib/identifier.js
generated
vendored
@@ -1,70 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.isIdentifierChar = isIdentifierChar;
|
||||
exports.isIdentifierName = isIdentifierName;
|
||||
exports.isIdentifierStart = isIdentifierStart;
|
||||
let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u0870-\u0887\u0889-\u088e\u08a0-\u08c9\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c5d\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cdd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u1711\u171f-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4c\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c8a\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7cd\ua7d0\ua7d1\ua7d3\ua7d5-\ua7dc\ua7f2-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
|
||||
let nonASCIIidentifierChars = "\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0897-\u089f\u08ca-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3c\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0cf3\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ece\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u180f-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf-\u1ace\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1dff\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\u30fb\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f\uff65";
|
||||
const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
|
||||
const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
|
||||
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
|
||||
const astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 4, 51, 13, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 39, 27, 10, 22, 251, 41, 7, 1, 17, 2, 60, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 31, 9, 2, 0, 3, 0, 2, 37, 2, 0, 26, 0, 2, 0, 45, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 200, 32, 32, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 328, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 26, 3994, 6, 582, 6842, 29, 1763, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 433, 44, 212, 63, 129, 74, 6, 0, 67, 12, 65, 1, 2, 0, 29, 6135, 9, 1237, 42, 9, 8936, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 229, 29, 3, 0, 496, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4153, 7, 221, 3, 5761, 15, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 4191];
|
||||
const astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 80, 3, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 343, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 726, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];
|
||||
function isInAstralSet(code, set) {
|
||||
let pos = 0x10000;
|
||||
for (let i = 0, length = set.length; i < length; i += 2) {
|
||||
pos += set[i];
|
||||
if (pos > code) return false;
|
||||
pos += set[i + 1];
|
||||
if (pos >= code) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isIdentifierStart(code) {
|
||||
if (code < 65) return code === 36;
|
||||
if (code <= 90) return true;
|
||||
if (code < 97) return code === 95;
|
||||
if (code <= 122) return true;
|
||||
if (code <= 0xffff) {
|
||||
return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
|
||||
}
|
||||
return isInAstralSet(code, astralIdentifierStartCodes);
|
||||
}
|
||||
function isIdentifierChar(code) {
|
||||
if (code < 48) return code === 36;
|
||||
if (code < 58) return true;
|
||||
if (code < 65) return false;
|
||||
if (code <= 90) return true;
|
||||
if (code < 97) return code === 95;
|
||||
if (code <= 122) return true;
|
||||
if (code <= 0xffff) {
|
||||
return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
|
||||
}
|
||||
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
|
||||
}
|
||||
function isIdentifierName(name) {
|
||||
let isFirst = true;
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
let cp = name.charCodeAt(i);
|
||||
if ((cp & 0xfc00) === 0xd800 && i + 1 < name.length) {
|
||||
const trail = name.charCodeAt(++i);
|
||||
if ((trail & 0xfc00) === 0xdc00) {
|
||||
cp = 0x10000 + ((cp & 0x3ff) << 10) + (trail & 0x3ff);
|
||||
}
|
||||
}
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
if (!isIdentifierStart(cp)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!isIdentifierChar(cp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !isFirst;
|
||||
}
|
||||
|
||||
//# sourceMappingURL=identifier.js.map
|
||||
1
frontend/node_modules/@babel/helper-validator-identifier/lib/identifier.js.map
generated
vendored
1
frontend/node_modules/@babel/helper-validator-identifier/lib/identifier.js.map
generated
vendored
File diff suppressed because one or more lines are too long
57
frontend/node_modules/@babel/helper-validator-identifier/lib/index.js
generated
vendored
57
frontend/node_modules/@babel/helper-validator-identifier/lib/index.js
generated
vendored
@@ -1,57 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "isIdentifierChar", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _identifier.isIdentifierChar;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "isIdentifierName", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _identifier.isIdentifierName;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "isIdentifierStart", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _identifier.isIdentifierStart;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "isKeyword", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _keyword.isKeyword;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "isReservedWord", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _keyword.isReservedWord;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "isStrictBindOnlyReservedWord", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _keyword.isStrictBindOnlyReservedWord;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "isStrictBindReservedWord", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _keyword.isStrictBindReservedWord;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(exports, "isStrictReservedWord", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _keyword.isStrictReservedWord;
|
||||
}
|
||||
});
|
||||
var _identifier = require("./identifier.js");
|
||||
var _keyword = require("./keyword.js");
|
||||
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
frontend/node_modules/@babel/helper-validator-identifier/lib/index.js.map
generated
vendored
1
frontend/node_modules/@babel/helper-validator-identifier/lib/index.js.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"names":["_identifier","require","_keyword"],"sources":["../src/index.ts"],"sourcesContent":["export {\n isIdentifierName,\n isIdentifierChar,\n isIdentifierStart,\n} from \"./identifier.ts\";\nexport {\n isReservedWord,\n isStrictBindOnlyReservedWord,\n isStrictBindReservedWord,\n isStrictReservedWord,\n isKeyword,\n} from \"./keyword.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAKA,IAAAC,QAAA,GAAAD,OAAA","ignoreList":[]}
|
||||
35
frontend/node_modules/@babel/helper-validator-identifier/lib/keyword.js
generated
vendored
35
frontend/node_modules/@babel/helper-validator-identifier/lib/keyword.js
generated
vendored
@@ -1,35 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.isKeyword = isKeyword;
|
||||
exports.isReservedWord = isReservedWord;
|
||||
exports.isStrictBindOnlyReservedWord = isStrictBindOnlyReservedWord;
|
||||
exports.isStrictBindReservedWord = isStrictBindReservedWord;
|
||||
exports.isStrictReservedWord = isStrictReservedWord;
|
||||
const reservedWords = {
|
||||
keyword: ["break", "case", "catch", "continue", "debugger", "default", "do", "else", "finally", "for", "function", "if", "return", "switch", "throw", "try", "var", "const", "while", "with", "new", "this", "super", "class", "extends", "export", "import", "null", "true", "false", "in", "instanceof", "typeof", "void", "delete"],
|
||||
strict: ["implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"],
|
||||
strictBind: ["eval", "arguments"]
|
||||
};
|
||||
const keywords = new Set(reservedWords.keyword);
|
||||
const reservedWordsStrictSet = new Set(reservedWords.strict);
|
||||
const reservedWordsStrictBindSet = new Set(reservedWords.strictBind);
|
||||
function isReservedWord(word, inModule) {
|
||||
return inModule && word === "await" || word === "enum";
|
||||
}
|
||||
function isStrictReservedWord(word, inModule) {
|
||||
return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);
|
||||
}
|
||||
function isStrictBindOnlyReservedWord(word) {
|
||||
return reservedWordsStrictBindSet.has(word);
|
||||
}
|
||||
function isStrictBindReservedWord(word, inModule) {
|
||||
return isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word);
|
||||
}
|
||||
function isKeyword(word) {
|
||||
return keywords.has(word);
|
||||
}
|
||||
|
||||
//# sourceMappingURL=keyword.js.map
|
||||
1
frontend/node_modules/@babel/helper-validator-identifier/lib/keyword.js.map
generated
vendored
1
frontend/node_modules/@babel/helper-validator-identifier/lib/keyword.js.map
generated
vendored
@@ -1 +0,0 @@
|
||||
{"version":3,"names":["reservedWords","keyword","strict","strictBind","keywords","Set","reservedWordsStrictSet","reservedWordsStrictBindSet","isReservedWord","word","inModule","isStrictReservedWord","has","isStrictBindOnlyReservedWord","isStrictBindReservedWord","isKeyword"],"sources":["../src/keyword.ts"],"sourcesContent":["const reservedWords = {\n keyword: [\n \"break\",\n \"case\",\n \"catch\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"do\",\n \"else\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"return\",\n \"switch\",\n \"throw\",\n \"try\",\n \"var\",\n \"const\",\n \"while\",\n \"with\",\n \"new\",\n \"this\",\n \"super\",\n \"class\",\n \"extends\",\n \"export\",\n \"import\",\n \"null\",\n \"true\",\n \"false\",\n \"in\",\n \"instanceof\",\n \"typeof\",\n \"void\",\n \"delete\",\n ],\n strict: [\n \"implements\",\n \"interface\",\n \"let\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"static\",\n \"yield\",\n ],\n strictBind: [\"eval\", \"arguments\"],\n};\nconst keywords = new Set(reservedWords.keyword);\nconst reservedWordsStrictSet = new Set(reservedWords.strict);\nconst reservedWordsStrictBindSet = new Set(reservedWords.strictBind);\n\n/**\n * Checks if word is a reserved word in non-strict mode\n */\nexport function isReservedWord(word: string, inModule: boolean): boolean {\n return (inModule && word === \"await\") || word === \"enum\";\n}\n\n/**\n * Checks if word is a reserved word in non-binding strict mode\n *\n * Includes non-strict reserved words\n */\nexport function isStrictReservedWord(word: string, inModule: boolean): boolean {\n return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);\n}\n\n/**\n * Checks if word is a reserved word in binding strict mode, but it is allowed as\n * a normal identifier.\n */\nexport function isStrictBindOnlyReservedWord(word: string): boolean {\n return reservedWordsStrictBindSet.has(word);\n}\n\n/**\n * Checks if word is a reserved word in binding strict mode\n *\n * Includes non-strict reserved words and non-binding strict reserved words\n */\nexport function isStrictBindReservedWord(\n word: string,\n inModule: boolean,\n): boolean {\n return (\n isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word)\n );\n}\n\nexport function isKeyword(word: string): boolean {\n return keywords.has(word);\n}\n"],"mappings":";;;;;;;;;;AAAA,MAAMA,aAAa,GAAG;EACpBC,OAAO,EAAE,CACP,OAAO,EACP,MAAM,EACN,OAAO,EACP,UAAU,EACV,UAAU,EACV,SAAS,EACT,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,KAAK,EACL,MAAM,EACN,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,QAAQ,CACT;EACDC,MAAM,EAAE,CACN,YAAY,EACZ,WAAW,EACX,KAAK,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,OAAO,CACR;EACDC,UAAU,EAAE,CAAC,MAAM,EAAE,WAAW;AAClC,CAAC;AACD,MAAMC,QAAQ,GAAG,IAAIC,GAAG,CAACL,aAAa,CAACC,OAAO,CAAC;AAC/C,MAAMK,sBAAsB,GAAG,IAAID,GAAG,CAACL,aAAa,CAACE,MAAM,CAAC;AAC5D,MAAMK,0BAA0B,GAAG,IAAIF,GAAG,CAACL,aAAa,CAACG,UAAU,CAAC;AAK7D,SAASK,cAAcA,CAACC,IAAY,EAAEC,QAAiB,EAAW;EACvE,OAAQA,QAAQ,IAAID,IAAI,KAAK,OAAO,IAAKA,IAAI,KAAK,MAAM;AAC1D;AAOO,SAASE,oBAAoBA,CAACF,IAAY,EAAEC,QAAiB,EAAW;EAC7E,OAAOF,cAAc,CAACC,IAAI,EAAEC,QAAQ,CAAC,IAAIJ,sBAAsB,CAACM,GAAG,CAACH,IAAI,CAAC;AAC3E;AAMO,SAASI,4BAA4BA,CAACJ,IAAY,EAAW;EAClE,OAAOF,0BAA0B,CAACK,GAAG,CAACH,IAAI,CAAC;AAC7C;AAOO,SAASK,wBAAwBA,CACtCL,IAAY,EACZC,QAAiB,EACR;EACT,OACEC,oBAAoB,CAACF,IAAI,EAAEC,QAAQ,CAAC,IAAIG,4BAA4B,CAACJ,IAAI,CAAC;AAE9E;AAEO,SAASM,SAASA,CAACN,IAAY,EAAW;EAC/C,OAAOL,QAAQ,CAACQ,GAAG,CAACH,IAAI,CAAC;AAC3B","ignoreList":[]}
|
||||
31
frontend/node_modules/@babel/helper-validator-identifier/package.json
generated
vendored
31
frontend/node_modules/@babel/helper-validator-identifier/package.json
generated
vendored
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "@babel/helper-validator-identifier",
|
||||
"version": "7.27.1",
|
||||
"description": "Validate identifier/keywords name",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-helper-validator-identifier"
|
||||
},
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@unicode/unicode-16.0.0": "^1.0.0",
|
||||
"charcodes": "^0.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"type": "commonjs"
|
||||
}
|
||||
1073
frontend/node_modules/@babel/parser/CHANGELOG.md
generated
vendored
1073
frontend/node_modules/@babel/parser/CHANGELOG.md
generated
vendored
File diff suppressed because it is too large
Load Diff
19
frontend/node_modules/@babel/parser/LICENSE
generated
vendored
19
frontend/node_modules/@babel/parser/LICENSE
generated
vendored
@@ -1,19 +0,0 @@
|
||||
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
19
frontend/node_modules/@babel/parser/README.md
generated
vendored
19
frontend/node_modules/@babel/parser/README.md
generated
vendored
@@ -1,19 +0,0 @@
|
||||
# @babel/parser
|
||||
|
||||
> A JavaScript parser
|
||||
|
||||
See our website [@babel/parser](https://babeljs.io/docs/babel-parser) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20parser%22+is%3Aopen) associated with this package.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/parser
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/parser --dev
|
||||
```
|
||||
14595
frontend/node_modules/@babel/parser/lib/index.js
generated
vendored
14595
frontend/node_modules/@babel/parser/lib/index.js
generated
vendored
File diff suppressed because it is too large
Load Diff
1
frontend/node_modules/@babel/parser/lib/index.js.map
generated
vendored
1
frontend/node_modules/@babel/parser/lib/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
50
frontend/node_modules/@babel/parser/package.json
generated
vendored
50
frontend/node_modules/@babel/parser/package.json
generated
vendored
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"name": "@babel/parser",
|
||||
"version": "7.28.4",
|
||||
"description": "A JavaScript parser",
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"homepage": "https://babel.dev/docs/en/next/babel-parser",
|
||||
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A+parser+%28babylon%29%22+is%3Aopen",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"babel",
|
||||
"javascript",
|
||||
"parser",
|
||||
"tc39",
|
||||
"ecmascript",
|
||||
"@babel/parser"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-parser"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"types": "./typings/babel-parser.d.ts",
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"typings/babel-parser.d.ts",
|
||||
"index.cjs"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"# dependencies": "This package doesn't actually have runtime dependencies. @babel/types is only needed for type definitions.",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/helper-check-duplicate-nodes": "^7.27.1",
|
||||
"@babel/helper-fixtures": "^7.28.0",
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"charcodes": "^0.2.0"
|
||||
},
|
||||
"bin": "./bin/babel-parser.js",
|
||||
"type": "commonjs"
|
||||
}
|
||||
239
frontend/node_modules/@babel/parser/typings/babel-parser.d.ts
generated
vendored
239
frontend/node_modules/@babel/parser/typings/babel-parser.d.ts
generated
vendored
@@ -1,239 +0,0 @@
|
||||
// This file is auto-generated! Do not modify it directly.
|
||||
// Run `yarn gulp bundle-dts` to re-generate it.
|
||||
/* eslint-disable @typescript-eslint/consistent-type-imports, @typescript-eslint/no-redundant-type-constituents */
|
||||
import { File, Expression } from '@babel/types';
|
||||
|
||||
type BABEL_8_BREAKING = false;
|
||||
type IF_BABEL_7<V> = false extends BABEL_8_BREAKING ? V : never;
|
||||
|
||||
type Plugin$1 =
|
||||
| "asyncDoExpressions"
|
||||
| IF_BABEL_7<"asyncGenerators">
|
||||
| IF_BABEL_7<"bigInt">
|
||||
| IF_BABEL_7<"classPrivateMethods">
|
||||
| IF_BABEL_7<"classPrivateProperties">
|
||||
| IF_BABEL_7<"classProperties">
|
||||
| IF_BABEL_7<"classStaticBlock">
|
||||
| IF_BABEL_7<"decimal">
|
||||
| "decorators-legacy"
|
||||
| "deferredImportEvaluation"
|
||||
| "decoratorAutoAccessors"
|
||||
| "destructuringPrivate"
|
||||
| "deprecatedImportAssert"
|
||||
| "doExpressions"
|
||||
| IF_BABEL_7<"dynamicImport">
|
||||
| IF_BABEL_7<"explicitResourceManagement">
|
||||
| "exportDefaultFrom"
|
||||
| IF_BABEL_7<"exportNamespaceFrom">
|
||||
| "flow"
|
||||
| "flowComments"
|
||||
| "functionBind"
|
||||
| "functionSent"
|
||||
| "importMeta"
|
||||
| "jsx"
|
||||
| IF_BABEL_7<"jsonStrings">
|
||||
| IF_BABEL_7<"logicalAssignment">
|
||||
| IF_BABEL_7<"importAssertions">
|
||||
| IF_BABEL_7<"importReflection">
|
||||
| "moduleBlocks"
|
||||
| IF_BABEL_7<"moduleStringNames">
|
||||
| IF_BABEL_7<"nullishCoalescingOperator">
|
||||
| IF_BABEL_7<"numericSeparator">
|
||||
| IF_BABEL_7<"objectRestSpread">
|
||||
| IF_BABEL_7<"optionalCatchBinding">
|
||||
| IF_BABEL_7<"optionalChaining">
|
||||
| "partialApplication"
|
||||
| "placeholders"
|
||||
| IF_BABEL_7<"privateIn">
|
||||
| IF_BABEL_7<"regexpUnicodeSets">
|
||||
| "sourcePhaseImports"
|
||||
| "throwExpressions"
|
||||
| IF_BABEL_7<"topLevelAwait">
|
||||
| "v8intrinsic"
|
||||
| ParserPluginWithOptions[0];
|
||||
|
||||
type ParserPluginWithOptions =
|
||||
| ["decorators", DecoratorsPluginOptions]
|
||||
| ["discardBinding", { syntaxType: "void" }]
|
||||
| ["estree", { classFeatures?: boolean }]
|
||||
| IF_BABEL_7<["importAttributes", { deprecatedAssertSyntax: boolean }]>
|
||||
| IF_BABEL_7<["moduleAttributes", { version: "may-2020" }]>
|
||||
| ["optionalChainingAssign", { version: "2023-07" }]
|
||||
| ["pipelineOperator", PipelineOperatorPluginOptions]
|
||||
| ["recordAndTuple", RecordAndTuplePluginOptions]
|
||||
| ["flow", FlowPluginOptions]
|
||||
| ["typescript", TypeScriptPluginOptions];
|
||||
|
||||
type PluginConfig = Plugin$1 | ParserPluginWithOptions;
|
||||
|
||||
interface DecoratorsPluginOptions {
|
||||
decoratorsBeforeExport?: boolean;
|
||||
allowCallParenthesized?: boolean;
|
||||
}
|
||||
|
||||
interface PipelineOperatorPluginOptions {
|
||||
proposal: BABEL_8_BREAKING extends false
|
||||
? "minimal" | "fsharp" | "hack" | "smart"
|
||||
: "fsharp" | "hack";
|
||||
topicToken?: "%" | "#" | "@@" | "^^" | "^";
|
||||
}
|
||||
|
||||
interface RecordAndTuplePluginOptions {
|
||||
syntaxType: "bar" | "hash";
|
||||
}
|
||||
|
||||
type FlowPluginOptions = BABEL_8_BREAKING extends true
|
||||
? {
|
||||
all?: boolean;
|
||||
enums?: boolean;
|
||||
}
|
||||
: {
|
||||
all?: boolean;
|
||||
};
|
||||
|
||||
interface TypeScriptPluginOptions {
|
||||
dts?: boolean;
|
||||
disallowAmbiguousJSXLike?: boolean;
|
||||
}
|
||||
|
||||
type Plugin = PluginConfig;
|
||||
|
||||
type SourceType = "script" | "commonjs" | "module" | "unambiguous";
|
||||
interface Options {
|
||||
/**
|
||||
* By default, import and export declarations can only appear at a program's top level.
|
||||
* Setting this option to true allows them anywhere where a statement is allowed.
|
||||
*/
|
||||
allowImportExportEverywhere?: boolean;
|
||||
/**
|
||||
* By default, await use is not allowed outside of an async function.
|
||||
* Set this to true to accept such code.
|
||||
*/
|
||||
allowAwaitOutsideFunction?: boolean;
|
||||
/**
|
||||
* By default, a return statement at the top level raises an error.
|
||||
* Set this to true to accept such code.
|
||||
*/
|
||||
allowReturnOutsideFunction?: boolean;
|
||||
/**
|
||||
* By default, new.target use is not allowed outside of a function or class.
|
||||
* Set this to true to accept such code.
|
||||
*/
|
||||
allowNewTargetOutsideFunction?: boolean;
|
||||
allowSuperOutsideMethod?: boolean;
|
||||
/**
|
||||
* By default, exported identifiers must refer to a declared variable.
|
||||
* Set this to true to allow export statements to reference undeclared variables.
|
||||
*/
|
||||
allowUndeclaredExports?: boolean;
|
||||
/**
|
||||
* By default, yield use is not allowed outside of a generator function.
|
||||
* Set this to true to accept such code.
|
||||
*/
|
||||
allowYieldOutsideFunction?: boolean;
|
||||
/**
|
||||
* By default, Babel parser JavaScript code according to Annex B syntax.
|
||||
* Set this to `false` to disable such behavior.
|
||||
*/
|
||||
annexB?: boolean;
|
||||
/**
|
||||
* By default, Babel attaches comments to adjacent AST nodes.
|
||||
* When this option is set to false, comments are not attached.
|
||||
* It can provide up to 30% performance improvement when the input code has many comments.
|
||||
* @babel/eslint-parser will set it for you.
|
||||
* It is not recommended to use attachComment: false with Babel transform,
|
||||
* as doing so removes all the comments in output code, and renders annotations such as
|
||||
* /* istanbul ignore next *\/ nonfunctional.
|
||||
*/
|
||||
attachComment?: boolean;
|
||||
/**
|
||||
* By default, Babel always throws an error when it finds some invalid code.
|
||||
* When this option is set to true, it will store the parsing error and
|
||||
* try to continue parsing the invalid input file.
|
||||
*/
|
||||
errorRecovery?: boolean;
|
||||
/**
|
||||
* Indicate the mode the code should be parsed in.
|
||||
* Can be one of "script", "commonjs", "module", or "unambiguous". Defaults to "script".
|
||||
* "unambiguous" will make @babel/parser attempt to guess, based on the presence
|
||||
* of ES6 import or export statements.
|
||||
* Files with ES6 imports and exports are considered "module" and are otherwise "script".
|
||||
*
|
||||
* Use "commonjs" to parse code that is intended to be run in a CommonJS environment such as Node.js.
|
||||
*/
|
||||
sourceType?: SourceType;
|
||||
/**
|
||||
* Correlate output AST nodes with their source filename.
|
||||
* Useful when generating code and source maps from the ASTs of multiple input files.
|
||||
*/
|
||||
sourceFilename?: string;
|
||||
/**
|
||||
* By default, all source indexes start from 0.
|
||||
* You can provide a start index to alternatively start with.
|
||||
* Useful for integration with other source tools.
|
||||
*/
|
||||
startIndex?: number;
|
||||
/**
|
||||
* By default, the first line of code parsed is treated as line 1.
|
||||
* You can provide a line number to alternatively start with.
|
||||
* Useful for integration with other source tools.
|
||||
*/
|
||||
startLine?: number;
|
||||
/**
|
||||
* By default, the parsed code is treated as if it starts from line 1, column 0.
|
||||
* You can provide a column number to alternatively start with.
|
||||
* Useful for integration with other source tools.
|
||||
*/
|
||||
startColumn?: number;
|
||||
/**
|
||||
* Array containing the plugins that you want to enable.
|
||||
*/
|
||||
plugins?: Plugin[];
|
||||
/**
|
||||
* Should the parser work in strict mode.
|
||||
* Defaults to true if sourceType === 'module'. Otherwise, false.
|
||||
*/
|
||||
strictMode?: boolean;
|
||||
/**
|
||||
* Adds a ranges property to each node: [node.start, node.end]
|
||||
*/
|
||||
ranges?: boolean;
|
||||
/**
|
||||
* Adds all parsed tokens to a tokens property on the File node.
|
||||
*/
|
||||
tokens?: boolean;
|
||||
/**
|
||||
* By default, the parser adds information about parentheses by setting
|
||||
* `extra.parenthesized` to `true` as needed.
|
||||
* When this option is `true` the parser creates `ParenthesizedExpression`
|
||||
* AST nodes instead of using the `extra` property.
|
||||
*/
|
||||
createParenthesizedExpressions?: boolean;
|
||||
/**
|
||||
* The default is false in Babel 7 and true in Babel 8
|
||||
* Set this to true to parse it as an `ImportExpression` node.
|
||||
* Otherwise `import(foo)` is parsed as `CallExpression(Import, [Identifier(foo)])`.
|
||||
*/
|
||||
createImportExpressions?: boolean;
|
||||
}
|
||||
|
||||
type ParserOptions = Partial<Options>;
|
||||
interface ParseError {
|
||||
code: string;
|
||||
reasonCode: string;
|
||||
}
|
||||
type ParseResult<Result extends File | Expression = File> = Result & {
|
||||
errors: null | ParseError[];
|
||||
};
|
||||
/**
|
||||
* Parse the provided code as an entire ECMAScript program.
|
||||
*/
|
||||
declare function parse(input: string, options?: ParserOptions): ParseResult<File>;
|
||||
declare function parseExpression(input: string, options?: ParserOptions): ParseResult<Expression>;
|
||||
|
||||
declare const tokTypes: {
|
||||
// todo(flow->ts) real token type
|
||||
[name: string]: any;
|
||||
};
|
||||
|
||||
export { DecoratorsPluginOptions, FlowPluginOptions, ParseError, ParseResult, ParserOptions, PluginConfig as ParserPlugin, ParserPluginWithOptions, PipelineOperatorPluginOptions, RecordAndTuplePluginOptions, TypeScriptPluginOptions, parse, parseExpression, tokTypes };
|
||||
22
frontend/node_modules/@babel/types/LICENSE
generated
vendored
22
frontend/node_modules/@babel/types/LICENSE
generated
vendored
@@ -1,22 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
19
frontend/node_modules/@babel/types/README.md
generated
vendored
19
frontend/node_modules/@babel/types/README.md
generated
vendored
@@ -1,19 +0,0 @@
|
||||
# @babel/types
|
||||
|
||||
> Babel Types is a Lodash-esque utility library for AST nodes
|
||||
|
||||
See our website [@babel/types](https://babeljs.io/docs/babel-types) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20types%22+is%3Aopen) associated with this package.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/types
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/types --dev
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user