leiyun 2 dagar sedan
förälder
incheckning
4c8b760dd7
6 ändrade filer med 565 tillägg och 34 borttagningar
  1. +1
    -0
      package.json
  2. +411
    -0
      pnpm-lock.yaml
  3. +6
    -0
      sql/sign_privacy_jhr.sql
  4. +53
    -11
      src/controller/admin/patient.js
  5. +62
    -11
      src/controller/mp.js
  6. +32
    -12
      src/service/screenshot.js

+ 1
- 0
package.json Visa fil

@@ -14,6 +14,7 @@
"cheerio": "^1.2.0",
"cos-nodejs-sdk-v5": "^2.14.0",
"dayjs": "^1.11.20",
"exceljs": "^4.4.0",
"jsonwebtoken": "^9.0.3",
"puppeteer": "^24.39.1",
"sharp": "^0.33.0",


+ 411
- 0
pnpm-lock.yaml Visa fil

@@ -20,6 +20,9 @@ importers:
dayjs:
specifier: ^1.11.20
version: 1.11.20
exceljs:
specifier: ^4.4.0
version: 4.4.0
jsonwebtoken:
specifier: ^9.0.3
version: 9.0.3
@@ -138,6 +141,12 @@ packages:
'@emnapi/runtime@1.8.1':
resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==}

'@fast-csv/format@4.3.5':
resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==}

'@fast-csv/parse@4.3.6':
resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==}

'@img/sharp-darwin-arm64@0.33.5':
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -285,6 +294,9 @@ packages:
'@types/keyv@3.1.4':
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}

'@types/node@14.18.63':
resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==}

'@types/node@25.2.3':
resolution: {integrity: sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==}

@@ -385,6 +397,18 @@ packages:
anymatch@1.3.2:
resolution: {integrity: sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==}

archiver-utils@2.1.0:
resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==}
engines: {node: '>= 6'}

archiver-utils@3.0.4:
resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==}
engines: {node: '>= 10'}

archiver@5.3.2:
resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==}
engines: {node: '>= 10'}

argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}

@@ -488,6 +512,9 @@ packages:
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
engines: {node: '>= 0.4'}

async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}

asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}

@@ -689,6 +716,9 @@ packages:
bare-url@2.4.0:
resolution: {integrity: sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==}

base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}

base@0.11.2:
resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
engines: {node: '>=0.10.0'}
@@ -700,6 +730,10 @@ packages:
bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}

big-integer@1.6.52:
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
engines: {node: '>=0.6'}

bignumber.js@9.0.0:
resolution: {integrity: sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==}

@@ -707,9 +741,18 @@ packages:
resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==}
engines: {node: '>=0.10.0'}

binary@0.3.0:
resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==}

bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}

bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}

bluebird@3.4.7:
resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==}

bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}

@@ -723,6 +766,9 @@ packages:
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}

brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}

braces@1.8.5:
resolution: {integrity: sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==}
engines: {node: '>=0.10.0'}
@@ -744,6 +790,17 @@ packages:
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}

buffer-indexof-polyfill@1.0.2:
resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==}
engines: {node: '>=0.10'}

buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}

buffers@0.1.1:
resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==}
engines: {node: '>=0.2.0'}

bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
@@ -806,6 +863,9 @@ packages:
caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}

chainsaw@0.1.0:
resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==}

chalk@0.4.0:
resolution: {integrity: sha512-sQfYDlfv2DGVtjdoQqxS0cEZDroyG8h6TamA6rvxwlrU5BaSLDx9xhatBYl2pxZ7gmpNaPFVwBtdGdu5rQ+tYQ==}
engines: {node: '>=0.8.0'}
@@ -939,6 +999,10 @@ packages:
component-emitter@1.3.1:
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}

compress-commons@4.1.2:
resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==}
engines: {node: '>= 10'}

concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}

@@ -1008,6 +1072,15 @@ packages:
typescript:
optional: true

crc-32@1.2.2:
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
engines: {node: '>=0.8'}
hasBin: true

crc32-stream@4.0.3:
resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==}
engines: {node: '>= 10'}

create-error-class@3.0.2:
resolution: {integrity: sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==}
engines: {node: '>=0.10.0'}
@@ -1414,6 +1487,10 @@ packages:
events-universal@1.0.1:
resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==}

exceljs@4.4.0:
resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==}
engines: {node: '>=8.3.0'}

execa@0.7.0:
resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==}
engines: {node: '>=4'}
@@ -1462,6 +1539,10 @@ packages:
resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
engines: {'0': node >=0.6.0}

fast-csv@4.3.6:
resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==}
engines: {node: '>=10.0.0'}

fast-deep-equal@1.1.0:
resolution: {integrity: sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==}

@@ -1589,6 +1670,9 @@ packages:
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
engines: {node: '>= 0.6'}

fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}

fs-extra@8.1.0:
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
engines: {node: '>=6 <7 || >=8'}
@@ -1602,6 +1686,11 @@ packages:
os: [darwin]
deprecated: Upgrade to fsevents v2 to mitigate potential security issues

fstream@1.0.12:
resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==}
engines: {node: '>=0.6'}
deprecated: This package is no longer supported.

function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}

@@ -1822,12 +1911,18 @@ packages:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}

ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}

ignore-by-default@1.0.1:
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}

ignore@3.3.10:
resolution: {integrity: sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==}

immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}

import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
@@ -2270,6 +2365,9 @@ packages:
resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
engines: {node: '>=0.6.0'}

jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}

jwa@2.0.1:
resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}

@@ -2319,13 +2417,23 @@ packages:
resolution: {integrity: sha512-Vn/JuGaYykbelAVNAhfVJxuwHQCOSNE6mqMtD+gnd+QORlAHwWVmVFqQga3yWt84G5vAwEwpT6sAsZ+tgJ88/Q==}
engines: {node: '>=0.10.0'}

lazystream@1.0.1:
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
engines: {node: '>= 0.6.3'}

levn@0.3.0:
resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
engines: {node: '>= 0.8.0'}

lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}

lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}

listenercount@1.0.1:
resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==}

load-json-file@1.1.0:
resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==}
engines: {node: '>=0.10.0'}
@@ -2349,12 +2457,21 @@ packages:
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}

lodash.defaults@4.2.0:
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}

lodash.difference@4.5.0:
resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==}

lodash.escaperegexp@4.1.2:
resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==}

lodash.flatten@4.4.0:
resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}

lodash.groupby@4.6.0:
resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==}

lodash.includes@4.3.0:
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}

@@ -2365,9 +2482,15 @@ packages:
resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.

lodash.isfunction@3.0.9:
resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==}

lodash.isinteger@4.0.4:
resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}

lodash.isnil@4.0.0:
resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==}

lodash.isnumber@3.0.3:
resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}

@@ -2377,12 +2500,21 @@ packages:
lodash.isstring@4.0.1:
resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}

lodash.isundefined@3.0.1:
resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==}

lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}

lodash.once@4.1.1:
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}

lodash.union@4.6.0:
resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}

lodash.uniq@4.5.0:
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}

lodash@4.17.23:
resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}

@@ -2501,6 +2633,10 @@ packages:
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}

minimatch@5.1.9:
resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==}
engines: {node: '>=10'}

minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}

@@ -2588,6 +2724,10 @@ packages:
resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==}
engines: {node: '>=0.10.0'}

normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}

npm-run-path@2.0.2:
resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
engines: {node: '>=4'}
@@ -2783,6 +2923,9 @@ packages:
resolution: {integrity: sha512-PRg65iXMTt/uK8Rfh5zvzkUbfAPitF17YaCY+IbHsYgksiLvtzWWTUildHth3mVaZ7871OJ7gtP4LBRBlmAdXg==}
engines: {node: '>=0.10.0'}

pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}

parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -3032,6 +3175,13 @@ packages:
readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}

readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}

readdir-glob@1.1.3:
resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}

readdirp@2.2.1:
resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==}
engines: {node: '>=0.10'}
@@ -3209,6 +3359,10 @@ packages:
resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==}
engines: {node: '>=11.0.0'}

saxes@5.0.1:
resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
engines: {node: '>=10'}

semver-diff@2.1.0:
resolution: {integrity: sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==}
engines: {node: '>=0.10.0'}
@@ -3246,6 +3400,9 @@ packages:
resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
engines: {node: '>=0.10.0'}

setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}

setprototypeof@1.1.0:
resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==}

@@ -3510,6 +3667,10 @@ packages:
tar-fs@3.1.2:
resolution: {integrity: sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==}

tar-stream@2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}

tar-stream@3.1.8:
resolution: {integrity: sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==}

@@ -3682,6 +3843,10 @@ packages:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}

tmp@0.2.5:
resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
engines: {node: '>=14.14'}

to-fast-properties@1.0.3:
resolution: {integrity: sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==}
engines: {node: '>=0.10.0'}
@@ -3709,6 +3874,9 @@ packages:
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}

traverse@0.3.9:
resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==}

trim-newlines@1.0.0:
resolution: {integrity: sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==}
engines: {node: '>=0.10.0'}
@@ -3804,6 +3972,9 @@ packages:
resolution: {integrity: sha512-pwCcjjhEcpW45JZIySExBHYv5Y9EeL2OIGEfrSKp2dMUFGFv4CpvZkwJbVge8OvGH2BNNtJBx67DuKuJhf+N5Q==}
engines: {node: '>=0.10'}

unzipper@0.10.14:
resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==}

update-notifier@1.0.3:
resolution: {integrity: sha512-iQSLFuxB2ZFAxXGN28DTxk/GNGlBmtqawvguYDtChAHI9Xjy0z7c7hpw6ywutK34SEDYTpLEsAM1ATMq5pcQsw==}
engines: {node: '>=0.10.0'}
@@ -3839,6 +4010,10 @@ packages:
resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==}
hasBin: true

uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true

uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
hasBin: true
@@ -3952,6 +4127,9 @@ packages:
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
engines: {node: '>=4.0'}

xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}

xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
@@ -3978,6 +4156,10 @@ packages:
resolution: {integrity: sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==}
engines: {node: '>= 4.0.0'}

zip-stream@4.1.1:
resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
engines: {node: '>= 10'}

zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}

@@ -4065,6 +4247,25 @@ snapshots:
tslib: 2.8.1
optional: true

'@fast-csv/format@4.3.5':
dependencies:
'@types/node': 14.18.63
lodash.escaperegexp: 4.1.2
lodash.isboolean: 3.0.3
lodash.isequal: 4.5.0
lodash.isfunction: 3.0.9
lodash.isnil: 4.0.0

'@fast-csv/parse@4.3.6':
dependencies:
'@types/node': 14.18.63
lodash.escaperegexp: 4.1.2
lodash.groupby: 4.6.0
lodash.isfunction: 3.0.9
lodash.isnil: 4.0.0
lodash.isundefined: 3.0.1
lodash.uniq: 4.5.0

'@img/sharp-darwin-arm64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.0.4
@@ -4179,6 +4380,8 @@ snapshots:
dependencies:
'@types/node': 25.2.3

'@types/node@14.18.63': {}

'@types/node@25.2.3':
dependencies:
undici-types: 7.16.0
@@ -4275,6 +4478,42 @@ snapshots:
micromatch: 2.3.11
normalize-path: 2.1.1

archiver-utils@2.1.0:
dependencies:
glob: 7.2.3
graceful-fs: 4.2.11
lazystream: 1.0.1
lodash.defaults: 4.2.0
lodash.difference: 4.5.0
lodash.flatten: 4.4.0
lodash.isplainobject: 4.0.6
lodash.union: 4.6.0
normalize-path: 3.0.0
readable-stream: 2.3.8

archiver-utils@3.0.4:
dependencies:
glob: 7.2.3
graceful-fs: 4.2.11
lazystream: 1.0.1
lodash.defaults: 4.2.0
lodash.difference: 4.5.0
lodash.flatten: 4.4.0
lodash.isplainobject: 4.0.6
lodash.union: 4.6.0
normalize-path: 3.0.0
readable-stream: 3.6.2

archiver@5.3.2:
dependencies:
archiver-utils: 2.1.0
async: 3.2.6
buffer-crc32: 0.2.13
readable-stream: 3.6.2
readdir-glob: 1.1.3
tar-stream: 2.2.0
zip-stream: 4.1.1

argparse@1.0.10:
dependencies:
sprintf-js: 1.0.3
@@ -4377,6 +4616,8 @@ snapshots:

async-function@1.0.0: {}

async@3.2.6: {}

asynckit@0.4.0: {}

atob@2.1.2: {}
@@ -4795,6 +5036,8 @@ snapshots:
dependencies:
bare-path: 3.0.0

base64-js@1.5.1: {}

base@0.11.2:
dependencies:
cache-base: 1.0.1
@@ -4811,15 +5054,30 @@ snapshots:
dependencies:
tweetnacl: 0.14.5

big-integer@1.6.52: {}

bignumber.js@9.0.0: {}

binary-extensions@1.13.1: {}

binary@0.3.0:
dependencies:
buffers: 0.1.1
chainsaw: 0.1.0

bindings@1.5.0:
dependencies:
file-uri-to-path: 1.0.0
optional: true

bl@4.1.0:
dependencies:
buffer: 5.7.1
inherits: 2.0.4
readable-stream: 3.6.2

bluebird@3.4.7: {}

bluebird@3.7.2: {}

boolbase@1.0.0: {}
@@ -4841,6 +5099,10 @@ snapshots:
balanced-match: 1.0.2
concat-map: 0.0.1

brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2

braces@1.8.5:
dependencies:
expand-range: 1.8.2
@@ -4870,6 +5132,15 @@ snapshots:

buffer-from@1.1.2: {}

buffer-indexof-polyfill@1.0.2: {}

buffer@5.7.1:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1

buffers@0.1.1: {}

bytes@3.1.2: {}

cache-base@1.0.1:
@@ -4940,6 +5211,10 @@ snapshots:

caseless@0.12.0: {}

chainsaw@0.1.0:
dependencies:
traverse: 0.3.9

chalk@0.4.0:
dependencies:
ansi-styles: 1.0.0
@@ -5095,6 +5370,13 @@ snapshots:

component-emitter@1.3.1: {}

compress-commons@4.1.2:
dependencies:
buffer-crc32: 0.2.13
crc32-stream: 4.0.3
normalize-path: 3.0.0
readable-stream: 3.6.2

concat-map@0.0.1: {}

concat-stream@1.6.2:
@@ -5177,6 +5459,13 @@ snapshots:
js-yaml: 4.1.1
parse-json: 5.2.0

crc-32@1.2.2: {}

crc32-stream@4.0.3:
dependencies:
crc-32: 1.2.2
readable-stream: 3.6.2

create-error-class@3.0.2:
dependencies:
capture-stack-trace: 1.0.2
@@ -5671,6 +5960,18 @@ snapshots:
transitivePeerDependencies:
- bare-abort-controller

exceljs@4.4.0:
dependencies:
archiver: 5.3.2
dayjs: 1.11.20
fast-csv: 4.3.6
jszip: 3.10.1
readable-stream: 3.6.2
saxes: 5.0.1
tmp: 0.2.5
unzipper: 0.10.14
uuid: 8.3.2

execa@0.7.0:
dependencies:
cross-spawn: 5.1.0
@@ -5747,6 +6048,11 @@ snapshots:

extsprintf@1.3.0: {}

fast-csv@4.3.6:
dependencies:
'@fast-csv/format': 4.3.5
'@fast-csv/parse': 4.3.6

fast-deep-equal@1.1.0: {}

fast-deep-equal@3.1.3: {}
@@ -5872,6 +6178,8 @@ snapshots:

fresh@0.5.2: {}

fs-constants@1.0.0: {}

fs-extra@8.1.0:
dependencies:
graceful-fs: 4.2.11
@@ -5886,6 +6194,13 @@ snapshots:
nan: 2.25.0
optional: true

fstream@1.0.12:
dependencies:
graceful-fs: 4.2.11
inherits: 2.0.4
mkdirp: 0.5.6
rimraf: 2.6.3

function-bind@1.1.2: {}

function.prototype.name@1.1.8:
@@ -6151,10 +6466,14 @@ snapshots:
dependencies:
safer-buffer: 2.1.2

ieee754@1.2.1: {}

ignore-by-default@1.0.1: {}

ignore@3.3.10: {}

immediate@3.0.6: {}

import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
@@ -6580,6 +6899,13 @@ snapshots:
json-schema: 0.4.0
verror: 1.10.0

jszip@3.10.1:
dependencies:
lie: 3.3.0
pako: 1.0.11
readable-stream: 2.3.8
setimmediate: 1.0.5

jwa@2.0.1:
dependencies:
buffer-equal-constant-time: 1.0.1
@@ -6659,13 +6985,23 @@ snapshots:

lazy-req@1.1.0: {}

lazystream@1.0.1:
dependencies:
readable-stream: 2.3.8

levn@0.3.0:
dependencies:
prelude-ls: 1.1.2
type-check: 0.3.2

lie@3.3.0:
dependencies:
immediate: 3.0.6

lines-and-columns@1.2.4: {}

listenercount@1.0.1: {}

load-json-file@1.1.0:
dependencies:
graceful-fs: 4.2.11
@@ -6700,28 +7036,44 @@ snapshots:

lodash.debounce@4.0.8: {}

lodash.defaults@4.2.0: {}

lodash.difference@4.5.0: {}

lodash.escaperegexp@4.1.2: {}

lodash.flatten@4.4.0: {}

lodash.groupby@4.6.0: {}

lodash.includes@4.3.0: {}

lodash.isboolean@3.0.3: {}

lodash.isequal@4.5.0: {}

lodash.isfunction@3.0.9: {}

lodash.isinteger@4.0.4: {}

lodash.isnil@4.0.0: {}

lodash.isnumber@3.0.3: {}

lodash.isplainobject@4.0.6: {}

lodash.isstring@4.0.1: {}

lodash.isundefined@3.0.1: {}

lodash.merge@4.6.2: {}

lodash.once@4.1.1: {}

lodash.union@4.6.0: {}

lodash.uniq@4.5.0: {}

lodash@4.17.23: {}

log4js@6.9.1:
@@ -6857,6 +7209,10 @@ snapshots:
dependencies:
brace-expansion: 1.1.12

minimatch@5.1.9:
dependencies:
brace-expansion: 2.0.2

minimist@1.2.8: {}

mitt@3.0.1: {}
@@ -6954,6 +7310,8 @@ snapshots:
dependencies:
remove-trailing-separator: 1.1.0

normalize-path@3.0.0: {}

npm-run-path@2.0.2:
dependencies:
path-key: 2.0.1
@@ -7139,6 +7497,8 @@ snapshots:
registry-url: 3.1.0
semver: 5.7.2

pako@1.0.11: {}

parent-module@1.0.1:
dependencies:
callsites: 3.1.0
@@ -7419,6 +7779,16 @@ snapshots:
string_decoder: 1.1.1
util-deprecate: 1.0.2

readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
string_decoder: 1.1.1
util-deprecate: 1.0.2

readdir-glob@1.1.3:
dependencies:
minimatch: 5.1.9

readdirp@2.2.1:
dependencies:
graceful-fs: 4.2.11
@@ -7611,6 +7981,10 @@ snapshots:

sax@1.4.4: {}

saxes@5.0.1:
dependencies:
xmlchars: 2.2.0

semver-diff@2.1.0:
dependencies:
semver: 5.7.2
@@ -7652,6 +8026,8 @@ snapshots:
is-plain-object: 2.0.4
split-string: 3.1.0

setimmediate@1.0.5: {}

setprototypeof@1.1.0: {}

setprototypeof@1.2.0: {}
@@ -7981,6 +8357,14 @@ snapshots:
- bare-buffer
- react-native-b4a

tar-stream@2.2.0:
dependencies:
bl: 4.1.0
end-of-stream: 1.4.5
fs-constants: 1.0.0
inherits: 2.0.4
readable-stream: 3.6.2

tar-stream@3.1.8:
dependencies:
b4a: 1.8.0
@@ -8298,6 +8682,8 @@ snapshots:
dependencies:
os-tmpdir: 1.0.2

tmp@0.2.5: {}

to-fast-properties@1.0.3: {}

to-object-path@0.3.0:
@@ -8325,6 +8711,8 @@ snapshots:

tr46@0.0.3: {}

traverse@0.3.9: {}

trim-newlines@1.0.0: {}

trim-right@1.0.1: {}
@@ -8431,6 +8819,19 @@ snapshots:

unzip-response@1.0.2: {}

unzipper@0.10.14:
dependencies:
big-integer: 1.6.52
binary: 0.3.0
bluebird: 3.4.7
buffer-indexof-polyfill: 1.0.2
duplexer2: 0.1.4
fstream: 1.0.12
graceful-fs: 4.2.11
listenercount: 1.0.1
readable-stream: 2.3.8
setimmediate: 1.0.5

update-notifier@1.0.3:
dependencies:
boxen: 0.6.0
@@ -8462,6 +8863,8 @@ snapshots:

uuid@7.0.3: {}

uuid@8.3.2: {}

uuid@9.0.1: {}

validate-npm-package-license@3.0.4:
@@ -8596,6 +8999,8 @@ snapshots:

xmlbuilder@11.0.1: {}

xmlchars@2.2.0: {}

xtend@4.0.2: {}

y18n@5.0.8: {}
@@ -8621,4 +9026,10 @@ snapshots:

ylru@1.4.0: {}

zip-stream@4.1.1:
dependencies:
archiver-utils: 3.0.4
compress-commons: 4.1.2
readable-stream: 3.6.2

zod@3.25.76: {}

+ 6
- 0
sql/sign_privacy_jhr.sql Visa fil

@@ -0,0 +1,6 @@
-- 患者表新增监护人签名字段
ALTER TABLE `patient` ADD COLUMN `sign_privacy_jhr` varchar(500) DEFAULT '' COMMENT '个人信息处理同意书(监护人)签名图URL' AFTER `sign_privacy`;

-- content表新增监护人签名协议内容
INSERT INTO `content` (`content_key`, `title`, `content`, `status`, `create_time`, `update_time`)
VALUES ('sign_privacy_jhr', '个人信息处理同意书(监护人)', '<p>请在此处填写监护人版个人信息处理同意书内容。</p>', 1, NOW(), NOW());

+ 53
- 11
src/controller/admin/patient.js Visa fil

@@ -338,9 +338,40 @@ module.exports = class extends Base {

const statusMap = { '-1': '待提交', 0: '待审核', 1: '审核通过', 2: '已驳回' };
const header = ['ID', '姓名', '性别', '身份证', '手机号', '省份', '城市', '审核状态', '审核日期', '审核驳回原因', '瘤种'];
const rows = list.map(item => {

const ExcelJS = require('exceljs');
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet('患者信息');

// 表头
sheet.addRow(header);
// 表头样式:绿色背景、白色加粗字体(仅到瘤种列,即K列=11列)、冻结首行
const headerRow = sheet.getRow(1);
const colCount = header.length; // 11列
for (let i = 1; i <= colCount; i++) {
const cell = headerRow.getCell(i);
cell.font = { bold: true, color: { argb: 'FFFFFFFF' } };
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF4CAF50' } };
cell.alignment = { vertical: 'middle', horizontal: 'center' };
}
sheet.views = [{ state: 'frozen', ySplit: 1 }];

// 边框样式
const thinBorder = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
// 表头加边框
for (let i = 1; i <= colCount; i++) {
headerRow.getCell(i).border = thinBorder;
}

// 数据行
list.forEach(item => {
const audit = auditMap[item.id];
return [
const row = sheet.addRow([
item.patient_no,
item.name,
item.gender,
@@ -352,18 +383,29 @@ module.exports = class extends Base {
audit ? (audit.create_time || '') : '',
(audit && audit.action === 'reject') ? (audit.reason || '') : '',
item.tag || ''
];
]);
// 数据行加边框
for (let i = 1; i <= colCount; i++) {
row.getCell(i).border = thinBorder;
}
});

// 自动列宽
sheet.columns.forEach(col => {
let maxLen = 10;
col.eachCell({ includeEmpty: true }, cell => {
const len = String(cell.value || '').length;
if (len > maxLen) maxLen = len;
});
col.width = Math.min(maxLen + 4, 40);
});

// 生成 CSV(带 BOM 以支持 Excel 中文)
const csvContent = [header, ...rows]
.map(row => row.map(cell => `"${String(cell || '').replace(/"/g, '""')}"`).join(','))
.join('\n');
const bom = '\uFEFF';
const buffer = await workbook.xlsx.writeBuffer();
const fileName = encodeURIComponent(`患者信息_${Date.now()}.xlsx`);

this.ctx.set('Content-Type', 'text/csv; charset=utf-8');
this.ctx.set('Content-Disposition', `attachment; filename=patients_${Date.now()}.csv`);
this.ctx.body = bom + csvContent;
this.ctx.set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
this.ctx.set('Content-Disposition', `attachment; filename*=UTF-8''${fileName}`);
this.ctx.body = Buffer.from(buffer);

await this.log('export', '患者管理', `导出患者数据 ${list.length} 条`);
}


+ 62
- 11
src/controller/mp.js Visa fil

@@ -163,7 +163,7 @@ module.exports = class extends Base {
async authSubmitAction() {
const mpUser = this.mpUser;
if (!mpUser) return this.json({ code: 1009, msg: '请先登录' });
const { idCardType, idCardFront, idCardBack, photo, realName, idCard, gender, birthday, issuingAuthority, validPeriod, mobile, code } = this.post();
const { idCardType, idCardFront, idCardBack, photo, realName, idCard, gender, birthday, issuingAuthority, validPeriod, mobile, code, confirmBind } = this.post();
if (!realName) return this.json({ code: 1, msg: '请输入证件姓名' });
if (!idCard) return this.json({ code: 1, msg: '请输入证件号码' });
const cardTypeInt = parseInt(idCardType) || 1;
@@ -182,11 +182,48 @@ module.exports = class extends Base {
const patientModel = this.model('patient');
const userModel = this.model('wechat_user');
const currentUser = await userModel.where({ id: mpUser.id }).find();
const existPatient = await patientModel.where({
id_card: idCard, is_deleted: 0,
...(currentUser.patient_id ? { id: ['!=', currentUser.patient_id] } : {})
}).find();
if (!think.isEmpty(existPatient)) return this.json({ code: 1, msg: '该证件号已被其他用户认证' });

// 查找已存在的患者(排除当前用户已绑定的)
const existCondition = { id_card: idCard, is_deleted: 0 };
if (currentUser.patient_id) existCondition.id = ['!=', currentUser.patient_id];
const existPatient = await patientModel.where(existCondition).find();

if (!think.isEmpty(existPatient)) {
// 手机号+身份证都匹配 → 可绑定已有患者
if (existPatient.phone === mobile) {
if (!confirmBind) {
// 首次提交,返回 1010 让前端确认
const maskedName = existPatient.name.length > 1
? existPatient.name[0] + '*'.repeat(existPatient.name.length - 1)
: existPatient.name;
const maskedPhone = '****' + existPatient.phone.slice(-4);
return this.json({
code: 1010,
data: { patientName: maskedName, patientPhone: maskedPhone },
msg: '该用户信息已存在'
});
}
// 用户确认绑定:检查该 patient 是否已被其他微信用户绑定
const boundUser = await userModel.where({ patient_id: existPatient.id, id: ['!=', mpUser.id], status: 1 }).find();
if (!think.isEmpty(boundUser)) {
return this.json({ code: 1, msg: '该患者信息已被其他微信账号绑定' });
}
// 绑定已有 patient 到当前微信用户
const now = think.datetime(new Date());
await userModel.where({ id: mpUser.id }).update({ patient_id: existPatient.id, update_time: now });
// 更新认证信息
await patientModel.where({ id: existPatient.id }).update({
name: realName, phone: mobile, id_card_type: cardTypeInt,
id_card_front: idCardFront || '', id_card_back: idCardBack || '', photo: photo || '',
gender: gender || '', birth_date: birthday || null,
issuing_authority: issuingAuthority || '', valid_period: validPeriod || '',
auth_status: 1, auth_time: now, update_time: now
});
return this.json({ code: 0, data: {}, msg: '实名认证成功' });
}
// 身份证匹配但手机号不同 → 真正的冲突
return this.json({ code: 1, msg: '该证件号已被其他用户认证' });
}

if (cardTypeInt === 1 || cardTypeInt === 3) {
const faceidConfig = require('../config/faceid.js');
@@ -266,6 +303,7 @@ module.exports = class extends Base {
tag: patient.tag || '', documents,
sign_income: patient.sign_income || '',
sign_privacy: patient.sign_privacy || '',
sign_privacy_jhr: patient.sign_privacy_jhr || '',
sign_promise: patient.sign_promise || '',
income_amount: patient.income_amount || '',
status: patient.status, auth_status: patient.auth_status || 0,
@@ -281,7 +319,7 @@ module.exports = class extends Base {
async saveMyInfoAction() {
const mpUser = this.mpUser;
if (!mpUser) return this.json({ code: 1009, msg: '请先登录' });
const { gender, province_code, city_code, district_code, address, emergency_contact, emergency_phone, documents, sign_income, sign_privacy, sign_promise, income_amount, mp_env_version } = this.post();
const { gender, province_code, city_code, district_code, address, emergency_contact, emergency_phone, documents, sign_income, sign_privacy, sign_privacy_jhr, sign_promise, income_amount, mp_env_version } = this.post();
const user = await this.model('wechat_user').where({ id: mpUser.id, status: 1 }).find();
if (think.isEmpty(user) || !user.patient_id) return this.json({ code: 1, msg: '请先完成实名认证' });
if (!province_code || !city_code || !district_code) return this.json({ code: 1, msg: '请选择省市区' });
@@ -300,6 +338,7 @@ module.exports = class extends Base {
documents: JSON.stringify(documents || []),
sign_income: sign_income || '',
sign_privacy: sign_privacy || '',
sign_privacy_jhr: sign_privacy_jhr || '',
sign_promise: sign_promise || '',
income_amount: income_amount || null,
status: 0,
@@ -368,13 +407,18 @@ module.exports = class extends Base {
async signAction() {
const mpUser = this.mpUser;
if (!mpUser) return this.json({ code: 1009, msg: '请先登录' });
const { type, signImage, amount } = this.post();
const validTypes = ['income', 'privacy', 'promise'];
const { type, signImage, amount, guardianName, guardianIdCard, guardianRelation } = this.post();
const validTypes = ['income', 'privacy', 'privacy_jhr', 'promise'];
if (!validTypes.includes(type)) return this.json({ code: 1, msg: '签署类型错误' });
if (!signImage) return this.json({ code: 1, msg: '请先签名' });
if (type === 'income' && (!amount || Number(amount) <= 0)) {
return this.json({ code: 1, msg: '请填写有效的收入金额' });
}
if (type === 'privacy_jhr') {
if (!guardianName) return this.json({ code: 1, msg: '请输入监护人姓名' });
if (!guardianIdCard) return this.json({ code: 1, msg: '请输入监护人身份证号' });
if (!guardianRelation) return this.json({ code: 1, msg: '请输入与患者关系' });
}

try {
// 获取患者姓名
@@ -392,7 +436,7 @@ module.exports = class extends Base {

// 调用截图服务生成合成图
const screenshotService = this.service('screenshot');
const url = await screenshotService.generate({
const generateParams = {
title: doc.title,
content: doc.content,
signImageUrl: signImage,
@@ -400,7 +444,14 @@ module.exports = class extends Base {
signerIdCard: patient.id_card,
signTime,
amount: type === 'income' ? amount : null
});
};
// 监护人类型传递额外字段
if (type === 'privacy_jhr') {
generateParams.guardianName = guardianName;
generateParams.guardianIdCard = guardianIdCard;
generateParams.guardianRelation = guardianRelation;
}
const url = await screenshotService.generate(generateParams);

return this.json({ code: 0, data: { url }, msg: '签署成功' });
} catch (error) {


+ 32
- 12
src/service/screenshot.js Visa fil

@@ -12,12 +12,15 @@ module.exports = class extends think.Service {
* @param {string} params.signerIdCard - 签署人身份证号
* @param {string} params.signTime - 签署时间
* @param {number} [params.amount] - 收入金额(仅income类型)
* @param {string} [params.guardianName] - 监护人姓名(仅privacy_jhr类型)
* @param {string} [params.guardianIdCard] - 监护人身份证号
* @param {string} [params.guardianRelation] - 与患者关系
* @returns {string} 合成图COS URL
*/
async generate({ title, content, signImageUrl, signerName, signerIdCard, signTime, amount }) {
async generate({ title, content, signImageUrl, signerName, signerIdCard, signTime, amount, guardianName, guardianIdCard, guardianRelation }) {
const puppeteer = require('puppeteer');

const html = this._buildHtml({ title, content, signImageUrl, signerName, signerIdCard, signTime, amount });
const html = this._buildHtml({ title, content, signImageUrl, signerName, signerIdCard, signTime, amount, guardianName, guardianIdCard, guardianRelation });

let browser;
try {
@@ -55,13 +58,38 @@ module.exports = class extends think.Service {
/**
* 构建 HTML 模板
*/
_buildHtml({ title, content, signImageUrl, signerName, signerIdCard, signTime, amount }) {
_buildHtml({ title, content, signImageUrl, signerName, signerIdCard, signTime, amount, guardianName, guardianIdCard, guardianRelation }) {
const amountHtml = amount ? `
<div style="margin: 30px 0; padding: 20px; background: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef;">
<span style="font-size: 28px; color: #555;">个人年可支配收入:</span>
<span style="font-size: 32px; color: #0e63e3; font-weight: 600;">¥${amount}</span>
</div>` : '';

// 监护人类型:特殊签署区布局
const isGuardian = !!(guardianName && guardianIdCard);
const signAreaHtml = isGuardian ? `
<div class="sign-area">
<div class="sign-row">
<span class="sign-label">监护人签字:</span>
<img class="sign-img" src="${signImageUrl}" />
</div>
<div class="sign-info">患者:${signerName}</div>
<div class="sign-info">患者身份证:${signerIdCard}</div>
<div class="sign-info">监护人姓名:${guardianName}</div>
<div class="sign-info">监护人身份证:${guardianIdCard}</div>
<div class="sign-info">与患者关系:${guardianRelation || ''}</div>
<div class="sign-info">签署时间:${signTime}</div>
</div>` : `
<div class="sign-area">
<div class="sign-row">
<span class="sign-label">签名:</span>
<img class="sign-img" src="${signImageUrl}" />
</div>
<div class="sign-info">签署人:${signerName}</div>
<div class="sign-info">身份证:${signerIdCard}</div>
<div class="sign-info">签署时间:${signTime}</div>
</div>`;

return `<!DOCTYPE html>
<html>
<head>
@@ -122,15 +150,7 @@ module.exports = class extends think.Service {
<div class="title">${title}</div>
<div class="content">${content}</div>
${amountHtml}
<div class="sign-area">
<div class="sign-row">
<span class="sign-label">签名:</span>
<img class="sign-img" src="${signImageUrl}" />
</div>
<div class="sign-info">签署人:${signerName}</div>
<div class="sign-info">身份证:${signerIdCard}</div>
<div class="sign-info">签署时间:${signTime}</div>
</div>
${signAreaHtml}
</body>
</html>`;
}


Laddar…
Avbryt
Spara