Init 1.0 codes

This commit is contained in:
2025-09-20 22:20:08 +08:00
commit 49922cc006
2294 changed files with 426627 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
# Diff Details
Date : 2025-07-08 12:45:16
Directory c:\\web\\app2
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details
## Files
| filename | language | code | comment | blank | total |
| :--- | :--- | ---: | ---: | ---: | ---: |
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details

View File

@@ -0,0 +1,2 @@
"filename", "language", "", "comment", "blank", "total"
"Total", "-", , 0, 0, 0
1 filename language comment blank total
2 Total - 0 0 0

View File

@@ -0,0 +1,19 @@
# Diff Summary
Date : 2025-07-08 12:45:16
Directory c:\\web\\app2
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)

View File

@@ -0,0 +1,22 @@
Date : 2025-07-08 12:45:16
Directory : c:\web\app2
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
Languages
+----------+------------+------------+------------+------------+------------+
| language | files | code | comment | blank | total |
+----------+------------+------------+------------+------------+------------+
+----------+------------+------------+------------+------------+------------+
Directories
+------+------------+------------+------------+------------+------------+
| path | files | code | comment | blank | total |
+------+------------+------------+------------+------------+------------+
+------+------------+------------+------------+------------+------------+
Files
+----------+----------+------------+------------+------------+------------+
| filename | language | code | comment | blank | total |
+----------+----------+------------+------------+------------+------------+
| Total | | 0 | 0 | 0 | 0 |
+----------+----------+------------+------------+------------+------------+

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,46 @@
# Summary
Date : 2025-07-08 12:45:16
Directory c:\\web\\app2
Total : 2118 files, 346723 codes, 1364 comments, 3318 blanks, all 351405 lines
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| JSON | 3 | 207,518 | 0 | 0 | 207,518 |
| YAML | 5 | 97,374 | 0 | 5 | 97,379 |
| JavaScript | 15 | 14,387 | 794 | 1,677 | 16,858 |
| XML | 2,023 | 8,089 | 15 | 0 | 8,104 |
| SCSS | 19 | 6,938 | 99 | 575 | 7,612 |
| Less | 18 | 6,907 | 72 | 570 | 7,549 |
| PHP | 27 | 4,887 | 323 | 384 | 5,594 |
| Python | 2 | 253 | 17 | 47 | 317 |
| MS SQL | 2 | 191 | 31 | 31 | 253 |
| CSS | 3 | 138 | 13 | 22 | 173 |
| Markdown | 1 | 41 | 0 | 7 | 48 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 2,118 | 346,723 | 1,364 | 3,318 | 351,405 |
| . (Files) | 13 | 1,816 | 173 | 204 | 2,193 |
| admin | 10 | 1,981 | 102 | 156 | 2,239 |
| css | 2 | 5 | 9 | 1 | 15 |
| developer | 7 | 1,417 | 80 | 83 | 1,580 |
| docs | 1 | 38 | 3 | 0 | 41 |
| js | 15 | 14,387 | 794 | 1,677 | 16,858 |
| less | 18 | 6,907 | 72 | 570 | 7,549 |
| metadata | 8 | 304,892 | 0 | 5 | 304,897 |
| scss | 19 | 6,938 | 99 | 575 | 7,612 |
| sprites | 3 | 6,069 | 15 | 0 | 6,084 |
| svgs | 2,020 | 2,020 | 0 | 0 | 2,020 |
| svgs\\brands | 467 | 467 | 0 | 0 | 467 |
| svgs\\regular | 163 | 163 | 0 | 0 | 163 |
| svgs\\solid | 1,390 | 1,390 | 0 | 0 | 1,390 |
| windows | 2 | 253 | 17 | 47 | 317 |
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
# Diff Details
Date : 2025-07-09 15:40:50
Directory c:\\web\\app2
Total : 105 files, 12779 codes, 4573 comments, 1852 blanks, all 19204 lines
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details
## Files
| filename | language | code | comment | blank | total |
| :--- | :--- | ---: | ---: | ---: | ---: |
| [.trae/rules/project_rules.md](/.trae/rules/project_rules.md) | Markdown | 4 | 0 | 3 | 7 |
| [README.md](/README.md) | Markdown | 248 | 0 | 47 | 295 |
| [admin/manage_developers.php](/admin/manage_developers.php) | PHP | 16 | 1 | 0 | 17 |
| [admin/review_apps.php](/admin/review_apps.php) | PHP | 61 | 4 | 4 | 69 |
| [api.php](/api.php) | PHP | 19 | 2 | 2 | 23 |
| [app.php](/app.php) | PHP | 95 | 7 | 9 | 111 |
| [app_store.sql](/app_store.sql) | MS SQL | 21 | 3 | 4 | 28 |
| [app_store_update.sql](/app_store_update.sql) | MS SQL | -2 | -1 | 0 | -3 |
| [composer.json](/composer.json) | JSON | 5 | 0 | 1 | 6 |
| [composer.lock](/composer.lock) | JSON | 100 | 0 | 1 | 101 |
| [config.php](/config.php) | PHP | -4 | 1 | 2 | -1 |
| [developer/dashboard.php](/developer/dashboard.php) | PHP | 1 | 0 | 0 | 1 |
| [developer/edit_app.php](/developer/edit_app.php) | PHP | -68 | 0 | 0 | -68 |
| [developer/profile.php](/developer/profile.php) | PHP | -3 | 0 | 1 | -2 |
| [developer/register.php](/developer/register.php) | PHP | 69 | 15 | 16 | 100 |
| [developer/upload_app.php](/developer/upload_app.php) | PHP | 55 | 2 | 6 | 63 |
| [developer/verify_email.php](/developer/verify_email.php) | PHP | 77 | 5 | 8 | 90 |
| [developer/version_control.php](/developer/version_control.php) | PHP | 209 | 10 | 13 | 232 |
| [developer_apps.php](/developer_apps.php) | PHP | 34 | 3 | 5 | 42 |
| [docs/app_review_standards.php](/docs/app_review_standards.php) | PHP | 196 | 0 | 18 | 214 |
| [docs/privacy_policy.php](/docs/privacy_policy.php) | PHP | 37 | 0 | 9 | 46 |
| [includes/logger.php](/includes/logger.php) | PHP | 30 | 18 | 8 | 56 |
| [index.php](/index.php) | PHP | -1 | 1 | 0 | 0 |
| [js/bootstrap.bundle.js](/js/bootstrap.bundle.js) | JavaScript | 5,053 | 655 | 598 | 6,306 |
| [mail/verification_template.php](/mail/verification_template.php) | PHP | 33 | 6 | 0 | 39 |
| [vendor/autoload.php](/vendor/autoload.php) | PHP | 20 | 1 | 5 | 26 |
| [vendor/composer/ClassLoader.php](/vendor/composer/ClassLoader.php) | PHP | 286 | 235 | 59 | 580 |
| [vendor/composer/InstalledVersions.php](/vendor/composer/InstalledVersions.php) | PHP | 178 | 133 | 49 | 360 |
| [vendor/composer/autoload_classmap.php](/vendor/composer/autoload_classmap.php) | PHP | 6 | 1 | 4 | 11 |
| [vendor/composer/autoload_namespaces.php](/vendor/composer/autoload_namespaces.php) | PHP | 5 | 1 | 4 | 10 |
| [vendor/composer/autoload_psr4.php](/vendor/composer/autoload_psr4.php) | PHP | 6 | 1 | 4 | 11 |
| [vendor/composer/autoload_real.php](/vendor/composer/autoload_real.php) | PHP | 25 | 4 | 10 | 39 |
| [vendor/composer/autoload_static.php](/vendor/composer/autoload_static.php) | PHP | 28 | 1 | 8 | 37 |
| [vendor/composer/installed.json](/vendor/composer/installed.json) | JSON | 90 | 0 | 1 | 91 |
| [vendor/composer/installed.php](/vendor/composer/installed.php) | PHP | 32 | 0 | 1 | 33 |
| [vendor/composer/platform_check.php](/vendor/composer/platform_check.php) | PHP | 21 | 1 | 5 | 27 |
| [vendor/phpmailer/phpmailer/README.md](/vendor/phpmailer/phpmailer/README.md) | Markdown | 169 | 0 | 64 | 233 |
| [vendor/phpmailer/phpmailer/SECURITY.md](/vendor/phpmailer/phpmailer/SECURITY.md) | Markdown | 19 | 0 | 19 | 38 |
| [vendor/phpmailer/phpmailer/SMTPUTF8.md](/vendor/phpmailer/phpmailer/SMTPUTF8.md) | Markdown | 27 | 0 | 22 | 49 |
| [vendor/phpmailer/phpmailer/composer.json](/vendor/phpmailer/phpmailer/composer.json) | JSON | 80 | 0 | 1 | 81 |
| [vendor/phpmailer/phpmailer/get_oauth_token.php](/vendor/phpmailer/phpmailer/get_oauth_token.php) | PHP | 117 | 48 | 18 | 183 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-af.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-af.php) | PHP | 20 | 4 | 3 | 27 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ar.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ar.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-as.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-as.php) | PHP | 28 | 5 | 3 | 36 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-az.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-az.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ba.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ba.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-be.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-be.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-bg.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-bg.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-bn.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-bn.php) | PHP | 28 | 5 | 3 | 36 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ca.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ca.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-cs.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-cs.php) | PHP | 22 | 4 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-da.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-da.php) | PHP | 27 | 7 | 3 | 37 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-de.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-de.php) | PHP | 22 | 4 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-el.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-el.php) | PHP | 27 | 4 | 3 | 34 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-eo.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-eo.php) | PHP | 20 | 4 | 3 | 27 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-es.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-es.php) | PHP | 27 | 7 | 3 | 37 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-et.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-et.php) | PHP | 20 | 6 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-fa.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-fa.php) | PHP | 20 | 6 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-fi.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-fi.php) | PHP | 13 | 12 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-fo.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-fo.php) | PHP | 13 | 12 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-fr.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-fr.php) | PHP | 27 | 7 | 3 | 37 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-gl.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-gl.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-he.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-he.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-hi.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-hi.php) | PHP | 27 | 6 | 3 | 36 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-hr.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-hr.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-hu.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-hu.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-hy.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-hy.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-id.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-id.php) | PHP | 22 | 7 | 3 | 32 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-it.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-it.php) | PHP | 20 | 6 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ja.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ja.php) | PHP | 27 | 8 | 3 | 38 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ka.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ka.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ko.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ko.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ku.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ku.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-lt.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-lt.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-lv.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-lv.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-mg.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-mg.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-mn.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-mn.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ms.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ms.php) | PHP | 20 | 5 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-nb.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-nb.php) | PHP | 27 | 4 | 3 | 34 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-nl.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-nl.php) | PHP | 27 | 5 | 3 | 35 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-pl.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-pl.php) | PHP | 27 | 4 | 3 | 34 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-pt.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-pt.php) | PHP | 27 | 5 | 3 | 35 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-pt_br.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-pt_br.php) | PHP | 27 | 9 | 3 | 39 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ro.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ro.php) | PHP | 27 | 4 | 3 | 34 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ru.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ru.php) | PHP | 27 | 7 | 3 | 37 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-si.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-si.php) | PHP | 27 | 5 | 3 | 35 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-sk.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-sk.php) | PHP | 22 | 6 | 3 | 31 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-sl.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-sl.php) | PHP | 27 | 7 | 3 | 37 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-sr.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-sr.php) | PHP | 20 | 6 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-sr_latn.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-sr_latn.php) | PHP | 20 | 6 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-sv.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-sv.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-tl.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-tl.php) | PHP | 20 | 6 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-tr.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-tr.php) | PHP | 27 | 9 | 3 | 39 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-uk.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-uk.php) | PHP | 20 | 6 | 3 | 29 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-ur.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-ur.php) | PHP | 23 | 5 | 3 | 31 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-vi.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-vi.php) | PHP | 19 | 6 | 3 | 28 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-zh.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-zh.php) | PHP | 20 | 7 | 3 | 30 |
| [vendor/phpmailer/phpmailer/language/phpmailer.lang-zh_cn.php](/vendor/phpmailer/phpmailer/language/phpmailer.lang-zh_cn.php) | PHP | 27 | 7 | 3 | 37 |
| [vendor/phpmailer/phpmailer/src/DSNConfigurator.php](/vendor/phpmailer/phpmailer/src/DSNConfigurator.php) | PHP | 136 | 80 | 30 | 246 |
| [vendor/phpmailer/phpmailer/src/Exception.php](/vendor/phpmailer/phpmailer/src/Exception.php) | PHP | 9 | 28 | 4 | 41 |
| [vendor/phpmailer/phpmailer/src/OAuth.php](/vendor/phpmailer/phpmailer/src/OAuth.php) | PHP | 46 | 79 | 15 | 140 |
| [vendor/phpmailer/phpmailer/src/OAuthTokenProvider.php](/vendor/phpmailer/phpmailer/src/OAuthTokenProvider.php) | PHP | 6 | 35 | 4 | 45 |
| [vendor/phpmailer/phpmailer/src/PHPMailer.php](/vendor/phpmailer/phpmailer/src/PHPMailer.php) | PHP | 2,985 | 1,979 | 399 | 5,363 |
| [vendor/phpmailer/phpmailer/src/POP3.php](/vendor/phpmailer/phpmailer/src/POP3.php) | PHP | 183 | 234 | 53 | 470 |
| [vendor/phpmailer/phpmailer/src/SMTP.php](/vendor/phpmailer/phpmailer/src/SMTP.php) | PHP | 757 | 644 | 147 | 1,548 |
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details

View File

@@ -0,0 +1,107 @@
"filename", "language", "PHP", "Markdown", "JSON", "JavaScript", "MS SQL", "comment", "blank", "total"
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/.trae\rules\project_rules.md", "Markdown", 0, 4, 0, 0, 0, 0, 3, 7
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/README.md", "Markdown", 0, 248, 0, 0, 0, 0, 47, 295
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/admin\manage_developers.php", "PHP", 16, 0, 0, 0, 0, 1, 0, 17
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/admin\review_apps.php", "PHP", 61, 0, 0, 0, 0, 4, 4, 69
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/api.php", "PHP", 19, 0, 0, 0, 0, 2, 2, 23
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/app.php", "PHP", 95, 0, 0, 0, 0, 7, 9, 111
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/app_store.sql", "MS SQL", 0, 0, 0, 0, 21, 3, 4, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/app_store_update.sql", "MS SQL", 0, 0, 0, 0, -2, -1, 0, -3
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/composer.json", "JSON", 0, 0, 5, 0, 0, 0, 1, 6
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/composer.lock", "JSON", 0, 0, 100, 0, 0, 0, 1, 101
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/config.php", "PHP", -4, 0, 0, 0, 0, 1, 2, -1
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\dashboard.php", "PHP", 1, 0, 0, 0, 0, 0, 0, 1
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\edit_app.php", "PHP", -68, 0, 0, 0, 0, 0, 0, -68
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\profile.php", "PHP", -3, 0, 0, 0, 0, 0, 1, -2
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\register.php", "PHP", 69, 0, 0, 0, 0, 15, 16, 100
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\upload_app.php", "PHP", 55, 0, 0, 0, 0, 2, 6, 63
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\verify_email.php", "PHP", 77, 0, 0, 0, 0, 5, 8, 90
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\version_control.php", "PHP", 209, 0, 0, 0, 0, 10, 13, 232
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/developer_apps.php", "PHP", 34, 0, 0, 0, 0, 3, 5, 42
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/docs\app_review_standards.php", "PHP", 196, 0, 0, 0, 0, 0, 18, 214
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/docs\privacy_policy.php", "PHP", 37, 0, 0, 0, 0, 0, 9, 46
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/includes/logger.php", "PHP", 30, 0, 0, 0, 0, 18, 8, 56
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/index.php", "PHP", -1, 0, 0, 0, 0, 1, 0, 0
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/js\bootstrap.bundle.js", "JavaScript", 0, 0, 0, 5053, 0, 655, 598, 6306
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/mail\verification_template.php", "PHP", 33, 0, 0, 0, 0, 6, 0, 39
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\autoload.php", "PHP", 20, 0, 0, 0, 0, 1, 5, 26
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\ClassLoader.php", "PHP", 286, 0, 0, 0, 0, 235, 59, 580
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\InstalledVersions.php", "PHP", 178, 0, 0, 0, 0, 133, 49, 360
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_classmap.php", "PHP", 6, 0, 0, 0, 0, 1, 4, 11
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_namespaces.php", "PHP", 5, 0, 0, 0, 0, 1, 4, 10
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_psr4.php", "PHP", 6, 0, 0, 0, 0, 1, 4, 11
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_real.php", "PHP", 25, 0, 0, 0, 0, 4, 10, 39
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_static.php", "PHP", 28, 0, 0, 0, 0, 1, 8, 37
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\installed.json", "JSON", 0, 0, 90, 0, 0, 0, 1, 91
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\installed.php", "PHP", 32, 0, 0, 0, 0, 0, 1, 33
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\platform_check.php", "PHP", 21, 0, 0, 0, 0, 1, 5, 27
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\README.md", "Markdown", 0, 169, 0, 0, 0, 0, 64, 233
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\SECURITY.md", "Markdown", 0, 19, 0, 0, 0, 0, 19, 38
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\SMTPUTF8.md", "Markdown", 0, 27, 0, 0, 0, 0, 22, 49
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\composer.json", "JSON", 0, 0, 80, 0, 0, 0, 1, 81
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\get_oauth_token.php", "PHP", 117, 0, 0, 0, 0, 48, 18, 183
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-af.php", "PHP", 20, 0, 0, 0, 0, 4, 3, 27
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ar.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-as.php", "PHP", 28, 0, 0, 0, 0, 5, 3, 36
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-az.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ba.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-be.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-bg.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-bn.php", "PHP", 28, 0, 0, 0, 0, 5, 3, 36
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ca.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-cs.php", "PHP", 22, 0, 0, 0, 0, 4, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-da.php", "PHP", 27, 0, 0, 0, 0, 7, 3, 37
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-de.php", "PHP", 22, 0, 0, 0, 0, 4, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-el.php", "PHP", 27, 0, 0, 0, 0, 4, 3, 34
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-eo.php", "PHP", 20, 0, 0, 0, 0, 4, 3, 27
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-es.php", "PHP", 27, 0, 0, 0, 0, 7, 3, 37
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-et.php", "PHP", 20, 0, 0, 0, 0, 6, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fa.php", "PHP", 20, 0, 0, 0, 0, 6, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fi.php", "PHP", 13, 0, 0, 0, 0, 12, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fo.php", "PHP", 13, 0, 0, 0, 0, 12, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fr.php", "PHP", 27, 0, 0, 0, 0, 7, 3, 37
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-gl.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-he.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hi.php", "PHP", 27, 0, 0, 0, 0, 6, 3, 36
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hr.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hu.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hy.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-id.php", "PHP", 22, 0, 0, 0, 0, 7, 3, 32
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-it.php", "PHP", 20, 0, 0, 0, 0, 6, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ja.php", "PHP", 27, 0, 0, 0, 0, 8, 3, 38
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ka.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ko.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ku.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-lt.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-lv.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-mg.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-mn.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ms.php", "PHP", 20, 0, 0, 0, 0, 5, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-nb.php", "PHP", 27, 0, 0, 0, 0, 4, 3, 34
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-nl.php", "PHP", 27, 0, 0, 0, 0, 5, 3, 35
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pl.php", "PHP", 27, 0, 0, 0, 0, 4, 3, 34
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pt.php", "PHP", 27, 0, 0, 0, 0, 5, 3, 35
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pt_br.php", "PHP", 27, 0, 0, 0, 0, 9, 3, 39
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ro.php", "PHP", 27, 0, 0, 0, 0, 4, 3, 34
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ru.php", "PHP", 27, 0, 0, 0, 0, 7, 3, 37
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-si.php", "PHP", 27, 0, 0, 0, 0, 5, 3, 35
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sk.php", "PHP", 22, 0, 0, 0, 0, 6, 3, 31
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sl.php", "PHP", 27, 0, 0, 0, 0, 7, 3, 37
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sr.php", "PHP", 20, 0, 0, 0, 0, 6, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sr_latn.php", "PHP", 20, 0, 0, 0, 0, 6, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sv.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-tl.php", "PHP", 20, 0, 0, 0, 0, 6, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-tr.php", "PHP", 27, 0, 0, 0, 0, 9, 3, 39
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-uk.php", "PHP", 20, 0, 0, 0, 0, 6, 3, 29
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ur.php", "PHP", 23, 0, 0, 0, 0, 5, 3, 31
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-vi.php", "PHP", 19, 0, 0, 0, 0, 6, 3, 28
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-zh.php", "PHP", 20, 0, 0, 0, 0, 7, 3, 30
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-zh_cn.php", "PHP", 27, 0, 0, 0, 0, 7, 3, 37
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\DSNConfigurator.php", "PHP", 136, 0, 0, 0, 0, 80, 30, 246
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\Exception.php", "PHP", 9, 0, 0, 0, 0, 28, 4, 41
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\OAuth.php", "PHP", 46, 0, 0, 0, 0, 79, 15, 140
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\OAuthTokenProvider.php", "PHP", 6, 0, 0, 0, 0, 35, 4, 45
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\PHPMailer.php", "PHP", 2985, 0, 0, 0, 0, 1979, 399, 5363
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\POP3.php", "PHP", 183, 0, 0, 0, 0, 234, 53, 470
"/www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\SMTP.php", "PHP", 757, 0, 0, 0, 0, 644, 147, 1548
"Total", "-", 6965, 467, 275, 5053, 19, 4573, 1852, 19204
1 filename language PHP Markdown JSON JavaScript MS SQL comment blank total
2 /www/wwwroot/leonmmcoset.jjxmm.win_8010/.trae\rules\project_rules.md Markdown 0 4 0 0 0 0 3 7
3 /www/wwwroot/leonmmcoset.jjxmm.win_8010/README.md Markdown 0 248 0 0 0 0 47 295
4 /www/wwwroot/leonmmcoset.jjxmm.win_8010/admin\manage_developers.php PHP 16 0 0 0 0 1 0 17
5 /www/wwwroot/leonmmcoset.jjxmm.win_8010/admin\review_apps.php PHP 61 0 0 0 0 4 4 69
6 /www/wwwroot/leonmmcoset.jjxmm.win_8010/api.php PHP 19 0 0 0 0 2 2 23
7 /www/wwwroot/leonmmcoset.jjxmm.win_8010/app.php PHP 95 0 0 0 0 7 9 111
8 /www/wwwroot/leonmmcoset.jjxmm.win_8010/app_store.sql MS SQL 0 0 0 0 21 3 4 28
9 /www/wwwroot/leonmmcoset.jjxmm.win_8010/app_store_update.sql MS SQL 0 0 0 0 -2 -1 0 -3
10 /www/wwwroot/leonmmcoset.jjxmm.win_8010/composer.json JSON 0 0 5 0 0 0 1 6
11 /www/wwwroot/leonmmcoset.jjxmm.win_8010/composer.lock JSON 0 0 100 0 0 0 1 101
12 /www/wwwroot/leonmmcoset.jjxmm.win_8010/config.php PHP -4 0 0 0 0 1 2 -1
13 /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\dashboard.php PHP 1 0 0 0 0 0 0 1
14 /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\edit_app.php PHP -68 0 0 0 0 0 0 -68
15 /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\profile.php PHP -3 0 0 0 0 0 1 -2
16 /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\register.php PHP 69 0 0 0 0 15 16 100
17 /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\upload_app.php PHP 55 0 0 0 0 2 6 63
18 /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\verify_email.php PHP 77 0 0 0 0 5 8 90
19 /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\version_control.php PHP 209 0 0 0 0 10 13 232
20 /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer_apps.php PHP 34 0 0 0 0 3 5 42
21 /www/wwwroot/leonmmcoset.jjxmm.win_8010/docs\app_review_standards.php PHP 196 0 0 0 0 0 18 214
22 /www/wwwroot/leonmmcoset.jjxmm.win_8010/docs\privacy_policy.php PHP 37 0 0 0 0 0 9 46
23 /www/wwwroot/leonmmcoset.jjxmm.win_8010/includes/logger.php PHP 30 0 0 0 0 18 8 56
24 /www/wwwroot/leonmmcoset.jjxmm.win_8010/index.php PHP -1 0 0 0 0 1 0 0
25 /www/wwwroot/leonmmcoset.jjxmm.win_8010/js\bootstrap.bundle.js JavaScript 0 0 0 5053 0 655 598 6306
26 /www/wwwroot/leonmmcoset.jjxmm.win_8010/mail\verification_template.php PHP 33 0 0 0 0 6 0 39
27 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\autoload.php PHP 20 0 0 0 0 1 5 26
28 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\ClassLoader.php PHP 286 0 0 0 0 235 59 580
29 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\InstalledVersions.php PHP 178 0 0 0 0 133 49 360
30 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_classmap.php PHP 6 0 0 0 0 1 4 11
31 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_namespaces.php PHP 5 0 0 0 0 1 4 10
32 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_psr4.php PHP 6 0 0 0 0 1 4 11
33 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_real.php PHP 25 0 0 0 0 4 10 39
34 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_static.php PHP 28 0 0 0 0 1 8 37
35 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\installed.json JSON 0 0 90 0 0 0 1 91
36 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\installed.php PHP 32 0 0 0 0 0 1 33
37 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\platform_check.php PHP 21 0 0 0 0 1 5 27
38 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\README.md Markdown 0 169 0 0 0 0 64 233
39 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\SECURITY.md Markdown 0 19 0 0 0 0 19 38
40 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\SMTPUTF8.md Markdown 0 27 0 0 0 0 22 49
41 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\composer.json JSON 0 0 80 0 0 0 1 81
42 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\get_oauth_token.php PHP 117 0 0 0 0 48 18 183
43 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-af.php PHP 20 0 0 0 0 4 3 27
44 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ar.php PHP 20 0 0 0 0 5 3 28
45 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-as.php PHP 28 0 0 0 0 5 3 36
46 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-az.php PHP 19 0 0 0 0 6 3 28
47 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ba.php PHP 20 0 0 0 0 5 3 28
48 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-be.php PHP 19 0 0 0 0 6 3 28
49 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-bg.php PHP 20 0 0 0 0 5 3 28
50 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-bn.php PHP 28 0 0 0 0 5 3 36
51 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ca.php PHP 19 0 0 0 0 6 3 28
52 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-cs.php PHP 22 0 0 0 0 4 3 29
53 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-da.php PHP 27 0 0 0 0 7 3 37
54 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-de.php PHP 22 0 0 0 0 4 3 29
55 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-el.php PHP 27 0 0 0 0 4 3 34
56 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-eo.php PHP 20 0 0 0 0 4 3 27
57 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-es.php PHP 27 0 0 0 0 7 3 37
58 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-et.php PHP 20 0 0 0 0 6 3 29
59 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fa.php PHP 20 0 0 0 0 6 3 29
60 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fi.php PHP 13 0 0 0 0 12 3 28
61 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fo.php PHP 13 0 0 0 0 12 3 28
62 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fr.php PHP 27 0 0 0 0 7 3 37
63 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-gl.php PHP 19 0 0 0 0 6 3 28
64 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-he.php PHP 19 0 0 0 0 6 3 28
65 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hi.php PHP 27 0 0 0 0 6 3 36
66 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hr.php PHP 20 0 0 0 0 5 3 28
67 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hu.php PHP 20 0 0 0 0 5 3 28
68 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hy.php PHP 20 0 0 0 0 5 3 28
69 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-id.php PHP 22 0 0 0 0 7 3 32
70 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-it.php PHP 20 0 0 0 0 6 3 29
71 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ja.php PHP 27 0 0 0 0 8 3 38
72 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ka.php PHP 20 0 0 0 0 5 3 28
73 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ko.php PHP 20 0 0 0 0 5 3 28
74 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ku.php PHP 20 0 0 0 0 5 3 28
75 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-lt.php PHP 19 0 0 0 0 6 3 28
76 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-lv.php PHP 19 0 0 0 0 6 3 28
77 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-mg.php PHP 20 0 0 0 0 5 3 28
78 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-mn.php PHP 20 0 0 0 0 5 3 28
79 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ms.php PHP 20 0 0 0 0 5 3 28
80 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-nb.php PHP 27 0 0 0 0 4 3 34
81 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-nl.php PHP 27 0 0 0 0 5 3 35
82 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pl.php PHP 27 0 0 0 0 4 3 34
83 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pt.php PHP 27 0 0 0 0 5 3 35
84 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pt_br.php PHP 27 0 0 0 0 9 3 39
85 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ro.php PHP 27 0 0 0 0 4 3 34
86 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ru.php PHP 27 0 0 0 0 7 3 37
87 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-si.php PHP 27 0 0 0 0 5 3 35
88 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sk.php PHP 22 0 0 0 0 6 3 31
89 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sl.php PHP 27 0 0 0 0 7 3 37
90 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sr.php PHP 20 0 0 0 0 6 3 29
91 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sr_latn.php PHP 20 0 0 0 0 6 3 29
92 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sv.php PHP 19 0 0 0 0 6 3 28
93 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-tl.php PHP 20 0 0 0 0 6 3 29
94 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-tr.php PHP 27 0 0 0 0 9 3 39
95 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-uk.php PHP 20 0 0 0 0 6 3 29
96 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ur.php PHP 23 0 0 0 0 5 3 31
97 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-vi.php PHP 19 0 0 0 0 6 3 28
98 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-zh.php PHP 20 0 0 0 0 7 3 30
99 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-zh_cn.php PHP 27 0 0 0 0 7 3 37
100 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\DSNConfigurator.php PHP 136 0 0 0 0 80 30 246
101 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\Exception.php PHP 9 0 0 0 0 28 4 41
102 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\OAuth.php PHP 46 0 0 0 0 79 15 140
103 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\OAuthTokenProvider.php PHP 6 0 0 0 0 35 4 45
104 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\PHPMailer.php PHP 2985 0 0 0 0 1979 399 5363
105 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\POP3.php PHP 183 0 0 0 0 234 53 470
106 /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\SMTP.php PHP 757 0 0 0 0 644 147 1548
107 Total - 6965 467 275 5053 19 4573 1852 19204

View File

@@ -0,0 +1,42 @@
# Diff Summary
Date : 2025-07-09 15:40:50
Directory c:\\web\\app2
Total : 105 files, 12779 codes, 4573 comments, 1852 blanks, all 19204 lines
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| PHP | 93 | 6,965 | 3,916 | 1,091 | 11,972 |
| JavaScript | 1 | 5,053 | 655 | 598 | 6,306 |
| Markdown | 5 | 467 | 0 | 155 | 622 |
| JSON | 4 | 275 | 0 | 4 | 279 |
| MS SQL | 2 | 19 | 2 | 4 | 25 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 105 | 12,779 | 4,573 | 1,852 | 19,204 |
| . (Files) | 10 | 515 | 16 | 71 | 602 |
| .trae | 1 | 4 | 0 | 3 | 7 |
| .trae\\rules | 1 | 4 | 0 | 3 | 7 |
| admin | 2 | 77 | 5 | 4 | 86 |
| developer | 7 | 340 | 32 | 44 | 416 |
| docs | 2 | 233 | 0 | 27 | 260 |
| includes | 1 | 30 | 18 | 8 | 56 |
| js | 1 | 5,053 | 655 | 598 | 6,306 |
| mail | 1 | 33 | 6 | 0 | 39 |
| vendor | 80 | 6,494 | 3,841 | 1,097 | 11,432 |
| vendor (Files) | 1 | 20 | 1 | 5 | 26 |
| vendor\\composer | 10 | 677 | 377 | 145 | 1,199 |
| vendor\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer (Files) | 5 | 412 | 48 | 124 | 584 |
| vendor\\phpmailer\\phpmailer\\language | 57 | 1,263 | 336 | 171 | 1,770 |
| vendor\\phpmailer\\phpmailer\\src | 7 | 4,122 | 3,079 | 652 | 7,853 |
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)

View File

@@ -0,0 +1,150 @@
Date : 2025-07-09 15:40:50
Directory : c:\web\app2
Total : 105 files, 12779 codes, 4573 comments, 1852 blanks, all 19204 lines
Languages
+------------+------------+------------+------------+------------+------------+
| language | files | code | comment | blank | total |
+------------+------------+------------+------------+------------+------------+
| PHP | 93 | 6,965 | 3,916 | 1,091 | 11,972 |
| JavaScript | 1 | 5,053 | 655 | 598 | 6,306 |
| Markdown | 5 | 467 | 0 | 155 | 622 |
| JSON | 4 | 275 | 0 | 4 | 279 |
| MS SQL | 2 | 19 | 2 | 4 | 25 |
+------------+------------+------------+------------+------------+------------+
Directories
+------------------------------------------------------------------------+------------+------------+------------+------------+------------+
| path | files | code | comment | blank | total |
+------------------------------------------------------------------------+------------+------------+------------+------------+------------+
| . | 105 | 12,779 | 4,573 | 1,852 | 19,204 |
| . (Files) | 10 | 515 | 16 | 71 | 602 |
| .trae | 1 | 4 | 0 | 3 | 7 |
| .trae\rules | 1 | 4 | 0 | 3 | 7 |
| admin | 2 | 77 | 5 | 4 | 86 |
| developer | 7 | 340 | 32 | 44 | 416 |
| docs | 2 | 233 | 0 | 27 | 260 |
| includes | 1 | 30 | 18 | 8 | 56 |
| js | 1 | 5,053 | 655 | 598 | 6,306 |
| mail | 1 | 33 | 6 | 0 | 39 |
| vendor | 80 | 6,494 | 3,841 | 1,097 | 11,432 |
| vendor (Files) | 1 | 20 | 1 | 5 | 26 |
| vendor\composer | 10 | 677 | 377 | 145 | 1,199 |
| vendor\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\phpmailer\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\phpmailer\phpmailer (Files) | 5 | 412 | 48 | 124 | 584 |
| vendor\phpmailer\phpmailer\language | 57 | 1,263 | 336 | 171 | 1,770 |
| vendor\phpmailer\phpmailer\src | 7 | 4,122 | 3,079 | 652 | 7,853 |
+------------------------------------------------------------------------+------------+------------+------------+------------+------------+
Files
+------------------------------------------------------------------------+------------+------------+------------+------------+------------+
| filename | language | code | comment | blank | total |
+------------------------------------------------------------------------+------------+------------+------------+------------+------------+
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/.trae\rules\project_rules.md | Markdown | 4 | 0 | 3 | 7 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/README.md | Markdown | 248 | 0 | 47 | 295 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/admin\manage_developers.php | PHP | 16 | 1 | 0 | 17 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/admin\review_apps.php | PHP | 61 | 4 | 4 | 69 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/api.php | PHP | 19 | 2 | 2 | 23 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/app.php | PHP | 95 | 7 | 9 | 111 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/app_store.sql | MS SQL | 21 | 3 | 4 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/app_store_update.sql | MS SQL | -2 | -1 | 0 | -3 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/composer.json | JSON | 5 | 0 | 1 | 6 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/composer.lock | JSON | 100 | 0 | 1 | 101 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/config.php | PHP | -4 | 1 | 2 | -1 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\dashboard.php | PHP | 1 | 0 | 0 | 1 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\edit_app.php | PHP | -68 | 0 | 0 | -68 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\profile.php | PHP | -3 | 0 | 1 | -2 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\register.php | PHP | 69 | 15 | 16 | 100 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\upload_app.php | PHP | 55 | 2 | 6 | 63 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\verify_email.php | PHP | 77 | 5 | 8 | 90 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer\version_control.php | PHP | 209 | 10 | 13 | 232 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/developer_apps.php | PHP | 34 | 3 | 5 | 42 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/docs\app_review_standards.php | PHP | 196 | 0 | 18 | 214 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/docs\privacy_policy.php | PHP | 37 | 0 | 9 | 46 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/includes/logger.php | PHP | 30 | 18 | 8 | 56 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/index.php | PHP | -1 | 1 | 0 | 0 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/js\bootstrap.bundle.js | JavaScript | 5,053 | 655 | 598 | 6,306 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/mail\verification_template.php | PHP | 33 | 6 | 0 | 39 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\autoload.php | PHP | 20 | 1 | 5 | 26 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\ClassLoader.php | PHP | 286 | 235 | 59 | 580 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\InstalledVersions.php | PHP | 178 | 133 | 49 | 360 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_classmap.php | PHP | 6 | 1 | 4 | 11 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_namespaces.php | PHP | 5 | 1 | 4 | 10 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_psr4.php | PHP | 6 | 1 | 4 | 11 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_real.php | PHP | 25 | 4 | 10 | 39 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\autoload_static.php | PHP | 28 | 1 | 8 | 37 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\installed.json | JSON | 90 | 0 | 1 | 91 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\installed.php | PHP | 32 | 0 | 1 | 33 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\composer\platform_check.php | PHP | 21 | 1 | 5 | 27 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\README.md | Markdown | 169 | 0 | 64 | 233 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\SECURITY.md | Markdown | 19 | 0 | 19 | 38 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\SMTPUTF8.md | Markdown | 27 | 0 | 22 | 49 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\composer.json | JSON | 80 | 0 | 1 | 81 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\get_oauth_token.php | PHP | 117 | 48 | 18 | 183 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-af.php | PHP | 20 | 4 | 3 | 27 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ar.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-as.php | PHP | 28 | 5 | 3 | 36 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-az.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ba.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-be.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-bg.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-bn.php | PHP | 28 | 5 | 3 | 36 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ca.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-cs.php | PHP | 22 | 4 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-da.php | PHP | 27 | 7 | 3 | 37 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-de.php | PHP | 22 | 4 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-el.php | PHP | 27 | 4 | 3 | 34 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-eo.php | PHP | 20 | 4 | 3 | 27 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-es.php | PHP | 27 | 7 | 3 | 37 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-et.php | PHP | 20 | 6 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fa.php | PHP | 20 | 6 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fi.php | PHP | 13 | 12 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fo.php | PHP | 13 | 12 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-fr.php | PHP | 27 | 7 | 3 | 37 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-gl.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-he.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hi.php | PHP | 27 | 6 | 3 | 36 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hr.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hu.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-hy.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-id.php | PHP | 22 | 7 | 3 | 32 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-it.php | PHP | 20 | 6 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ja.php | PHP | 27 | 8 | 3 | 38 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ka.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ko.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ku.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-lt.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-lv.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-mg.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-mn.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ms.php | PHP | 20 | 5 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-nb.php | PHP | 27 | 4 | 3 | 34 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-nl.php | PHP | 27 | 5 | 3 | 35 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pl.php | PHP | 27 | 4 | 3 | 34 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pt.php | PHP | 27 | 5 | 3 | 35 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-pt_br.php | PHP | 27 | 9 | 3 | 39 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ro.php | PHP | 27 | 4 | 3 | 34 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ru.php | PHP | 27 | 7 | 3 | 37 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-si.php | PHP | 27 | 5 | 3 | 35 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sk.php | PHP | 22 | 6 | 3 | 31 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sl.php | PHP | 27 | 7 | 3 | 37 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sr.php | PHP | 20 | 6 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sr_latn.php | PHP | 20 | 6 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-sv.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-tl.php | PHP | 20 | 6 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-tr.php | PHP | 27 | 9 | 3 | 39 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-uk.php | PHP | 20 | 6 | 3 | 29 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-ur.php | PHP | 23 | 5 | 3 | 31 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-vi.php | PHP | 19 | 6 | 3 | 28 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-zh.php | PHP | 20 | 7 | 3 | 30 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\language\phpmailer.lang-zh_cn.php | PHP | 27 | 7 | 3 | 37 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\DSNConfigurator.php | PHP | 136 | 80 | 30 | 246 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\Exception.php | PHP | 9 | 28 | 4 | 41 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\OAuth.php | PHP | 46 | 79 | 15 | 140 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\OAuthTokenProvider.php | PHP | 6 | 35 | 4 | 45 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\PHPMailer.php | PHP | 2,985 | 1,979 | 399 | 5,363 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\POP3.php | PHP | 183 | 234 | 53 | 470 |
| /www/wwwroot/leonmmcoset.jjxmm.win_8010/vendor\phpmailer\phpmailer\src\SMTP.php | PHP | 757 | 644 | 147 | 1,548 |
| Total | | 12,779 | 4,573 | 1,852 | 19,204 |
+------------------------------------------------------------------------+------------+------------+------------+------------+------------+

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,58 @@
# Summary
Date : 2025-07-09 15:40:50
Directory c:\\web\\app2
Total : 2206 files, 359502 codes, 5937 comments, 5170 blanks, all 370609 lines
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| JSON | 7 | 207,793 | 0 | 4 | 207,797 |
| YAML | 5 | 97,374 | 0 | 5 | 97,379 |
| JavaScript | 16 | 19,440 | 1,449 | 2,275 | 23,164 |
| PHP | 107 | 11,852 | 4,239 | 1,475 | 17,566 |
| XML | 2,023 | 8,089 | 15 | 0 | 8,104 |
| SCSS | 19 | 6,938 | 99 | 575 | 7,612 |
| Less | 18 | 6,907 | 72 | 570 | 7,549 |
| Markdown | 5 | 508 | 0 | 162 | 670 |
| Python | 2 | 253 | 17 | 47 | 317 |
| MS SQL | 1 | 210 | 33 | 35 | 278 |
| CSS | 3 | 138 | 13 | 22 | 173 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 2,206 | 359,502 | 5,937 | 5,170 | 370,609 |
| . (Files) | 14 | 2,331 | 189 | 275 | 2,795 |
| .trae | 1 | 4 | 0 | 3 | 7 |
| .trae\\rules | 1 | 4 | 0 | 3 | 7 |
| admin | 10 | 2,058 | 107 | 160 | 2,325 |
| css | 2 | 5 | 9 | 1 | 15 |
| developer | 9 | 1,757 | 112 | 127 | 1,996 |
| docs | 2 | 271 | 3 | 27 | 301 |
| includes | 1 | 30 | 18 | 8 | 56 |
| js | 16 | 19,440 | 1,449 | 2,275 | 23,164 |
| less | 18 | 6,907 | 72 | 570 | 7,549 |
| mail | 1 | 33 | 6 | 0 | 39 |
| metadata | 8 | 304,892 | 0 | 5 | 304,897 |
| scss | 19 | 6,938 | 99 | 575 | 7,612 |
| sprites | 3 | 6,069 | 15 | 0 | 6,084 |
| svgs | 2,020 | 2,020 | 0 | 0 | 2,020 |
| svgs\\brands | 467 | 467 | 0 | 0 | 467 |
| svgs\\regular | 163 | 163 | 0 | 0 | 163 |
| svgs\\solid | 1,390 | 1,390 | 0 | 0 | 1,390 |
| vendor | 80 | 6,494 | 3,841 | 1,097 | 11,432 |
| vendor (Files) | 1 | 20 | 1 | 5 | 26 |
| vendor\\composer | 10 | 677 | 377 | 145 | 1,199 |
| vendor\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer (Files) | 5 | 412 | 48 | 124 | 584 |
| vendor\\phpmailer\\phpmailer\\language | 57 | 1,263 | 336 | 171 | 1,770 |
| vendor\\phpmailer\\phpmailer\\src | 7 | 4,122 | 3,079 | 652 | 7,853 |
| windows | 2 | 253 | 17 | 47 | 317 |
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,90 @@
# Diff Summary
Date : 2025-07-12 21:22:51
Directory c:\\web\\app2
Total : 4416 files, 1117 codes, 65 comments, 56 blanks, all 1238 lines
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| PHP | 215 | 966 | 47 | 61 | 1,074 |
| HTML | 2 | 123 | 0 | 0 | 123 |
| Python | 4 | 70 | 8 | 10 | 88 |
| Ini | 1 | 11 | 9 | 4 | 24 |
| MS SQL | 2 | 8 | 1 | 1 | 10 |
| CSS | 6 | 3 | 0 | 1 | 4 |
| JSON | 14 | 0 | 0 | 0 | 0 |
| SCSS | 38 | 0 | 0 | 0 | 0 |
| XML | 4,046 | 0 | 0 | 0 | 0 |
| YAML | 10 | 0 | 0 | 0 | 0 |
| Less | 36 | 0 | 0 | 0 | 0 |
| JavaScript | 32 | 0 | 0 | 0 | 0 |
| Markdown | 10 | -64 | 0 | -21 | -85 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 2,210 | 360,619 | 6,002 | 5,226 | 371,847 |
| . (Files) | 15 | 2,612 | 227 | 287 | 3,126 |
| .trae | 1 | 4 | 0 | 3 | 7 |
| .trae\\rules | 1 | 4 | 0 | 3 | 7 |
| admin | 11 | 2,353 | 109 | 168 | 2,630 |
| css | 2 | 5 | 9 | 1 | 15 |
| d:\\ | 2,206 | -359,502 | -5,937 | -5,170 | -370,609 |
| d:\\app2 | 2,206 | -359,502 | -5,937 | -5,170 | -370,609 |
| d:\\app2 (Files) | 14 | -2,331 | -189 | -275 | -2,795 |
| d:\\app2\\.trae | 1 | -4 | 0 | -3 | -7 |
| d:\\app2\\.trae\\rules | 1 | -4 | 0 | -3 | -7 |
| d:\\app2\\admin | 10 | -2,058 | -107 | -160 | -2,325 |
| d:\\app2\\css | 2 | -5 | -9 | -1 | -15 |
| d:\\app2\\developer | 9 | -1,757 | -112 | -127 | -1,996 |
| d:\\app2\\docs | 2 | -271 | -3 | -27 | -301 |
| d:\\app2\\includes | 1 | -30 | -18 | -8 | -56 |
| d:\\app2\\js | 16 | -19,440 | -1,449 | -2,275 | -23,164 |
| d:\\app2\\less | 18 | -6,907 | -72 | -570 | -7,549 |
| d:\\app2\\mail | 1 | -33 | -6 | 0 | -39 |
| d:\\app2\\metadata | 8 | -304,892 | 0 | -5 | -304,897 |
| d:\\app2\\scss | 19 | -6,938 | -99 | -575 | -7,612 |
| d:\\app2\\sprites | 3 | -6,069 | -15 | 0 | -6,084 |
| d:\\app2\\svgs | 2,020 | -2,020 | 0 | 0 | -2,020 |
| d:\\app2\\svgs\\brands | 467 | -467 | 0 | 0 | -467 |
| d:\\app2\\svgs\\regular | 163 | -163 | 0 | 0 | -163 |
| d:\\app2\\svgs\\solid | 1,390 | -1,390 | 0 | 0 | -1,390 |
| d:\\app2\\vendor | 80 | -6,494 | -3,841 | -1,097 | -11,432 |
| d:\\app2\\vendor (Files) | 1 | -20 | -1 | -5 | -26 |
| d:\\app2\\vendor\\composer | 10 | -677 | -377 | -145 | -1,199 |
| d:\\app2\\vendor\\phpmailer | 69 | -5,797 | -3,463 | -947 | -10,207 |
| d:\\app2\\vendor\\phpmailer\\phpmailer | 69 | -5,797 | -3,463 | -947 | -10,207 |
| d:\\app2\\vendor\\phpmailer\\phpmailer (Files) | 5 | -412 | -48 | -124 | -584 |
| d:\\app2\\vendor\\phpmailer\\phpmailer\\language | 57 | -1,263 | -336 | -171 | -1,770 |
| d:\\app2\\vendor\\phpmailer\\phpmailer\\src | 7 | -4,122 | -3,079 | -652 | -7,853 |
| d:\\app2\\windows | 2 | -253 | -17 | -47 | -317 |
| developer | 9 | 2,101 | 129 | 153 | 2,383 |
| docs | 2 | 275 | 3 | 27 | 305 |
| error_pages | 2 | 123 | 0 | 0 | 123 |
| includes | 1 | 30 | 18 | 8 | 56 |
| js | 16 | 19,440 | 1,449 | 2,275 | 23,164 |
| less | 18 | 6,907 | 72 | 570 | 7,549 |
| mail | 1 | 33 | 6 | 0 | 39 |
| metadata | 8 | 304,892 | 0 | 5 | 304,897 |
| scss | 19 | 6,938 | 99 | 575 | 7,612 |
| sprites | 3 | 6,069 | 15 | 0 | 6,084 |
| svgs | 2,020 | 2,020 | 0 | 0 | 2,020 |
| svgs\\brands | 467 | 467 | 0 | 0 | 467 |
| svgs\\regular | 163 | 163 | 0 | 0 | 163 |
| svgs\\solid | 1,390 | 1,390 | 0 | 0 | 1,390 |
| vendor | 80 | 6,494 | 3,841 | 1,097 | 11,432 |
| vendor (Files) | 1 | 20 | 1 | 5 | 26 |
| vendor\\composer | 10 | 677 | 377 | 145 | 1,199 |
| vendor\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer (Files) | 5 | 412 | 48 | 124 | 584 |
| vendor\\phpmailer\\phpmailer\\language | 57 | 1,263 | 336 | 171 | 1,770 |
| vendor\\phpmailer\\phpmailer\\src | 7 | 4,122 | 3,079 | 652 | 7,853 |
| windows | 2 | 323 | 25 | 57 | 405 |
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
# Summary
Date : 2025-07-12 21:22:51
Directory c:\\web\\app2
Total : 2210 files, 360619 codes, 6002 comments, 5226 blanks, all 371847 lines
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| JSON | 7 | 207,793 | 0 | 4 | 207,797 |
| YAML | 5 | 97,374 | 0 | 5 | 97,379 |
| JavaScript | 16 | 19,440 | 1,449 | 2,275 | 23,164 |
| PHP | 108 | 12,818 | 4,286 | 1,536 | 18,640 |
| XML | 2,023 | 8,089 | 15 | 0 | 8,104 |
| SCSS | 19 | 6,938 | 99 | 575 | 7,612 |
| Less | 18 | 6,907 | 72 | 570 | 7,549 |
| Markdown | 5 | 444 | 0 | 141 | 585 |
| Python | 2 | 323 | 25 | 57 | 405 |
| MS SQL | 1 | 218 | 34 | 36 | 288 |
| CSS | 3 | 141 | 13 | 23 | 177 |
| HTML | 2 | 123 | 0 | 0 | 123 |
| Ini | 1 | 11 | 9 | 4 | 24 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 2,210 | 360,619 | 6,002 | 5,226 | 371,847 |
| . (Files) | 15 | 2,612 | 227 | 287 | 3,126 |
| .trae | 1 | 4 | 0 | 3 | 7 |
| .trae\\rules | 1 | 4 | 0 | 3 | 7 |
| admin | 11 | 2,353 | 109 | 168 | 2,630 |
| css | 2 | 5 | 9 | 1 | 15 |
| developer | 9 | 2,101 | 129 | 153 | 2,383 |
| docs | 2 | 275 | 3 | 27 | 305 |
| error_pages | 2 | 123 | 0 | 0 | 123 |
| includes | 1 | 30 | 18 | 8 | 56 |
| js | 16 | 19,440 | 1,449 | 2,275 | 23,164 |
| less | 18 | 6,907 | 72 | 570 | 7,549 |
| mail | 1 | 33 | 6 | 0 | 39 |
| metadata | 8 | 304,892 | 0 | 5 | 304,897 |
| scss | 19 | 6,938 | 99 | 575 | 7,612 |
| sprites | 3 | 6,069 | 15 | 0 | 6,084 |
| svgs | 2,020 | 2,020 | 0 | 0 | 2,020 |
| svgs\\brands | 467 | 467 | 0 | 0 | 467 |
| svgs\\regular | 163 | 163 | 0 | 0 | 163 |
| svgs\\solid | 1,390 | 1,390 | 0 | 0 | 1,390 |
| vendor | 80 | 6,494 | 3,841 | 1,097 | 11,432 |
| vendor (Files) | 1 | 20 | 1 | 5 | 26 |
| vendor\\composer | 10 | 677 | 377 | 145 | 1,199 |
| vendor\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer (Files) | 5 | 412 | 48 | 124 | 584 |
| vendor\\phpmailer\\phpmailer\\language | 57 | 1,263 | 336 | 171 | 1,770 |
| vendor\\phpmailer\\phpmailer\\src | 7 | 4,122 | 3,079 | 652 | 7,853 |
| windows | 2 | 323 | 25 | 57 | 405 |
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
# Diff Summary
Date : 2025-09-18 21:28:59
Directory d:\\Projects\\PHP\\APP Store
Total : 4442 files, -1191 codes, -516 comments, -444 blanks, all -2151 lines
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| XML | 4,047 | 2,166 | 0 | 1 | 2,167 |
| PHP | 229 | 1,516 | 115 | 146 | 1,777 |
| YAML | 13 | 113 | 0 | 2 | 115 |
| Markdown | 11 | 11 | 0 | 2 | 13 |
| pip requirements | 1 | 2 | 0 | 0 | 2 |
| CSS | 6 | 1 | 5 | 0 | 6 |
| JSON | 14 | 0 | 0 | 0 | 0 |
| Ini | 2 | 0 | 0 | 0 | 0 |
| Python | 4 | 0 | 0 | 0 | 0 |
| SCSS | 38 | 0 | 0 | 0 | 0 |
| Less | 36 | 0 | 0 | 0 | 0 |
| HTML | 4 | 0 | 0 | 0 | 0 |
| MS SQL | 3 | -8 | 0 | 1 | -7 |
| JavaScript | 34 | -4,992 | -636 | -596 | -6,224 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 2,232 | 359,428 | 5,486 | 4,782 | 369,696 |
| . (Files) | 24 | 5,671 | 296 | 376 | 6,343 |
| .github | 3 | 113 | 0 | 2 | 115 |
| .github\\ISSUE_TEMPLATE | 3 | 113 | 0 | 2 | 115 |
| .trae | 1 | 4 | 0 | 3 | 7 |
| .trae\\rules | 1 | 4 | 0 | 3 | 7 |
| admin | 15 | 2,749 | 142 | 211 | 3,102 |
| c:\\ | 2,210 | -360,619 | -6,002 | -5,226 | -371,847 |
| c:\\web | 2,210 | -360,619 | -6,002 | -5,226 | -371,847 |
| c:\\web\\app2 | 2,210 | -360,619 | -6,002 | -5,226 | -371,847 |
| c:\\web\\app2 (Files) | 15 | -2,612 | -227 | -287 | -3,126 |
| c:\\web\\app2\\.trae | 1 | -4 | 0 | -3 | -7 |
| c:\\web\\app2\\.trae\\rules | 1 | -4 | 0 | -3 | -7 |
| c:\\web\\app2\\admin | 11 | -2,353 | -109 | -168 | -2,630 |
| c:\\web\\app2\\css | 2 | -5 | -9 | -1 | -15 |
| c:\\web\\app2\\developer | 9 | -2,101 | -129 | -153 | -2,383 |
| c:\\web\\app2\\docs | 2 | -275 | -3 | -27 | -305 |
| c:\\web\\app2\\error_pages | 2 | -123 | 0 | 0 | -123 |
| c:\\web\\app2\\includes | 1 | -30 | -18 | -8 | -56 |
| c:\\web\\app2\\js | 16 | -19,440 | -1,449 | -2,275 | -23,164 |
| c:\\web\\app2\\less | 18 | -6,907 | -72 | -570 | -7,549 |
| c:\\web\\app2\\mail | 1 | -33 | -6 | 0 | -39 |
| c:\\web\\app2\\metadata | 8 | -304,892 | 0 | -5 | -304,897 |
| c:\\web\\app2\\scss | 19 | -6,938 | -99 | -575 | -7,612 |
| c:\\web\\app2\\sprites | 3 | -6,069 | -15 | 0 | -6,084 |
| c:\\web\\app2\\svgs | 2,020 | -2,020 | 0 | 0 | -2,020 |
| c:\\web\\app2\\svgs\\brands | 467 | -467 | 0 | 0 | -467 |
| c:\\web\\app2\\svgs\\regular | 163 | -163 | 0 | 0 | -163 |
| c:\\web\\app2\\svgs\\solid | 1,390 | -1,390 | 0 | 0 | -1,390 |
| c:\\web\\app2\\vendor | 80 | -6,494 | -3,841 | -1,097 | -11,432 |
| c:\\web\\app2\\vendor (Files) | 1 | -20 | -1 | -5 | -26 |
| c:\\web\\app2\\vendor\\composer | 10 | -677 | -377 | -145 | -1,199 |
| c:\\web\\app2\\vendor\\phpmailer | 69 | -5,797 | -3,463 | -947 | -10,207 |
| c:\\web\\app2\\vendor\\phpmailer\\phpmailer | 69 | -5,797 | -3,463 | -947 | -10,207 |
| c:\\web\\app2\\vendor\\phpmailer\\phpmailer (Files) | 5 | -412 | -48 | -124 | -584 |
| c:\\web\\app2\\vendor\\phpmailer\\phpmailer\\language | 57 | -1,263 | -336 | -171 | -1,770 |
| c:\\web\\app2\\vendor\\phpmailer\\phpmailer\\src | 7 | -4,122 | -3,079 | -652 | -7,853 |
| c:\\web\\app2\\windows | 2 | -323 | -25 | -57 | -405 |
| css | 2 | 6 | 14 | 1 | 21 |
| developer | 10 | 2,252 | 141 | 169 | 2,562 |
| docs | 4 | 354 | 4 | 29 | 387 |
| docs (Files) | 3 | 342 | 4 | 28 | 374 |
| docs\\markdown | 1 | 12 | 0 | 1 | 13 |
| error_pages | 2 | 123 | 0 | 0 | 123 |
| includes | 1 | 30 | 18 | 8 | 56 |
| js | 18 | 14,448 | 813 | 1,679 | 16,940 |
| less | 18 | 6,907 | 72 | 570 | 7,549 |
| mail | 1 | 33 | 6 | 0 | 39 |
| metadata | 8 | 304,892 | 0 | 5 | 304,897 |
| scss | 19 | 6,938 | 99 | 575 | 7,612 |
| sprites | 3 | 6,069 | 15 | 0 | 6,084 |
| svgs | 2,020 | 2,020 | 0 | 0 | 2,020 |
| svgs\\brands | 467 | 467 | 0 | 0 | 467 |
| svgs\\regular | 163 | 163 | 0 | 0 | 163 |
| svgs\\solid | 1,390 | 1,390 | 0 | 0 | 1,390 |
| vendor | 80 | 6,494 | 3,841 | 1,097 | 11,432 |
| vendor (Files) | 1 | 20 | 1 | 5 | 26 |
| vendor\\composer | 10 | 677 | 377 | 145 | 1,199 |
| vendor\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer (Files) | 5 | 412 | 48 | 124 | 584 |
| vendor\\phpmailer\\phpmailer\\language | 57 | 1,263 | 336 | 171 | 1,770 |
| vendor\\phpmailer\\phpmailer\\src | 7 | 4,122 | 3,079 | 652 | 7,853 |
| windows | 3 | 325 | 25 | 57 | 407 |
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,66 @@
# Summary
Date : 2025-09-18 21:28:59
Directory d:\\Projects\\PHP\\APP Store
Total : 2232 files, 359428 codes, 5486 comments, 4782 blanks, all 369696 lines
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
## Languages
| language | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| JSON | 7 | 207,793 | 0 | 4 | 207,797 |
| YAML | 8 | 97,487 | 0 | 7 | 97,494 |
| JavaScript | 18 | 14,448 | 813 | 1,679 | 16,940 |
| PHP | 121 | 14,334 | 4,401 | 1,682 | 20,417 |
| XML | 2,024 | 10,255 | 15 | 1 | 10,271 |
| SCSS | 19 | 6,938 | 99 | 575 | 7,612 |
| Less | 18 | 6,907 | 72 | 570 | 7,549 |
| Markdown | 6 | 455 | 0 | 143 | 598 |
| Python | 2 | 323 | 25 | 57 | 405 |
| MS SQL | 2 | 210 | 34 | 37 | 281 |
| CSS | 3 | 142 | 18 | 23 | 183 |
| HTML | 2 | 123 | 0 | 0 | 123 |
| Ini | 1 | 11 | 9 | 4 | 24 |
| pip requirements | 1 | 2 | 0 | 0 | 2 |
## Directories
| path | files | code | comment | blank | total |
| :--- | ---: | ---: | ---: | ---: | ---: |
| . | 2,232 | 359,428 | 5,486 | 4,782 | 369,696 |
| . (Files) | 24 | 5,671 | 296 | 376 | 6,343 |
| .github | 3 | 113 | 0 | 2 | 115 |
| .github\\ISSUE_TEMPLATE | 3 | 113 | 0 | 2 | 115 |
| .trae | 1 | 4 | 0 | 3 | 7 |
| .trae\\rules | 1 | 4 | 0 | 3 | 7 |
| admin | 15 | 2,749 | 142 | 211 | 3,102 |
| css | 2 | 6 | 14 | 1 | 21 |
| developer | 10 | 2,252 | 141 | 169 | 2,562 |
| docs | 4 | 354 | 4 | 29 | 387 |
| docs (Files) | 3 | 342 | 4 | 28 | 374 |
| docs\\markdown | 1 | 12 | 0 | 1 | 13 |
| error_pages | 2 | 123 | 0 | 0 | 123 |
| includes | 1 | 30 | 18 | 8 | 56 |
| js | 18 | 14,448 | 813 | 1,679 | 16,940 |
| less | 18 | 6,907 | 72 | 570 | 7,549 |
| mail | 1 | 33 | 6 | 0 | 39 |
| metadata | 8 | 304,892 | 0 | 5 | 304,897 |
| scss | 19 | 6,938 | 99 | 575 | 7,612 |
| sprites | 3 | 6,069 | 15 | 0 | 6,084 |
| svgs | 2,020 | 2,020 | 0 | 0 | 2,020 |
| svgs\\brands | 467 | 467 | 0 | 0 | 467 |
| svgs\\regular | 163 | 163 | 0 | 0 | 163 |
| svgs\\solid | 1,390 | 1,390 | 0 | 0 | 1,390 |
| vendor | 80 | 6,494 | 3,841 | 1,097 | 11,432 |
| vendor (Files) | 1 | 20 | 1 | 5 | 26 |
| vendor\\composer | 10 | 677 | 377 | 145 | 1,199 |
| vendor\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer | 69 | 5,797 | 3,463 | 947 | 10,207 |
| vendor\\phpmailer\\phpmailer (Files) | 5 | 412 | 48 | 124 | 584 |
| vendor\\phpmailer\\phpmailer\\language | 57 | 1,263 | 336 | 171 | 1,770 |
| vendor\\phpmailer\\phpmailer\\src | 7 | 4,122 | 3,079 | 652 | 7,853 |
| windows | 3 | 325 | 25 | 57 | 407 |
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)

File diff suppressed because it is too large Load Diff

65
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,65 @@
name: Bug报告
about: 创建一个Bug报告以帮助我们改进
title: '[BUG] '
labels: bug
assignees: ''
body:
- type: textarea
id: description
attributes:
label: 问题描述
description: 请清晰简洁地描述这个Bug是什么
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: 复现步骤
description: 复现这个Bug的步骤
placeholder: |
1. 打开页面 '...'
2. 点击 '....'
3. 滚动到 '....'
4. 看到错误
validations:
required: true
- type: textarea
id: expected
attributes:
label: 预期行为
description: 清晰简洁地描述你期望发生的事情
validations:
required: true
- type: textarea
id: actual
attributes:
label: 实际行为
description: 清晰简洁地描述实际发生的事情
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: 截图
description: 如果适用,添加截图以帮助解释你的问题
validations:
required: false
- type: textarea
id: environment
attributes:
label: 环境信息
description: 请提供你的环境信息
placeholder: |
操作系统: Windows 10
浏览器: Chrome 90.0.4430.212
设备: 桌面
validations:
required: false
- type: textarea
id: additional
attributes:
label: 其他信息
description: 在此处添加有关问题的任何其他上下文
validations:
required: false

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: true
templates:
- name: Bug报告
about: 创建一个Bug报告以帮助我们改进
file: bug_report.yml
- name: 功能请求
about: 为这个项目提出一个想法
file: feature_request.yml

View File

@@ -0,0 +1,42 @@
name: 功能请求
about: 为这个项目提出一个想法
title: '[FEATURE] '
labels: enhancement
assignees: ''
body:
- type: textarea
id: feature_description
attributes:
label: 功能描述
description: 清晰简洁地描述你想要的功能是什么
validations:
required: true
- type: textarea
id: use_case
attributes:
label: 使用场景
description: 描述这个功能将如何被使用,以及它解决了什么问题
validations:
required: true
- type: textarea
id: implementation
attributes:
label: 可能的实现方式
description: 如果你有想法,描述你希望这个功能如何实现
validations:
required: false
- type: textarea
id: alternatives
attributes:
label: 替代方案
description: 描述你考虑过的任何替代解决方案或功能
validations:
required: false
- type: textarea
id: additional
attributes:
label: 其他信息
description: 在此处添加有关功能请求的任何其他上下文或截图
validations:
required: false

16
.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
# 忽略上传的应用文件
uploads/
# 忽略文件目录
files/
# 忽略图片目录
images/
# 忽略日志文件
logs/
# 忽略Windows系统文件
Thumbs.db
.DS_Store
config.php

16
.htaccess Normal file
View File

@@ -0,0 +1,16 @@
# 增加请求体大小限制
LimitRequestBody 524288000
# 错误处理
ErrorDocument 500 /error_pages/500.html
ErrorDocument 404 /error_pages/404.html
# API 伪静态规则
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^api(/.*)?$ api.php?action=$1 [QSA,L]
Header append X-Powered-By "LeonAPP Server"
Header append X-System-Discription "LeonAPP Server System, a powerful APP store system."
Header append X-System-Author "LeonMMcoset"
Header append X-System-Email "leonmmcoset@outlook.com"

View File

@@ -0,0 +1,7 @@
弹窗都用Sweet Alert弹窗
说中文!!!
不要用通用顶栏方式搞顶栏
不要出现EndOfFile错误

21
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,21 @@
{
"git.ignoreLimitWarning": true,
"git.enableSmartCommit": true,
"git.autofetch": true,
"git.confirmSync": false,
"git.path": "git",
"git.terminalAuthentication": true,
"git.ignoreMissingGitWarning": false,
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"config.php": true
}
}

BIN
APP Store.zip Normal file

Binary file not shown.

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 LeonMMcoset
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.

258
README.md Normal file
View File

@@ -0,0 +1,258 @@
# App Store 项目
这是一个基于 PHP 7.4 的 App Store 项目,使用 Bootstrap 实现 Fluent Design 风格界面,数据存储采用 MySQL 数据库。项目各页面顶栏已添加 logo 图片,路径为 `/favicon.jpeg`,点击可跳转至首页,图片设置了高度 30px、右边距 10px 以及圆角样式 `var(--border-radius)`
## 项目结构
```
app2/
├── config.php # 配置文件,包含数据库和管理员信息
├── app_store.sql # 数据库初始化 SQL 文件
├── index.php # 首页
├── app.php # App 信息页
├── admin/ # 管理员后台目录
│ ├── addapp.php
│ ├── deleteapp.php
│ ├── editapp.php
│ ├── index.php
│ ├── login.php
│ ├── manage_tags.php
│ ├── review_apps.php
│ └── system_info.php
├── developer/ # 开发者后台目录
│ ├── dashboard.php
│ ├── edit_app.php
│ ├── login.php
│ ├── logout.php
│ ├── profile.php
│ ├── register.php
│ └── upload_app.php
├── vendor/ # Composer 依赖
├── includes/ # 通用包含文件
├── api.php # API 接口文件
├── styles.css # 自定义 CSS 文件
├── images/ # 存储 App 预览图片和年龄分级 SVG
│ ├── age_3plus.svg
│ ├── age_7plus.svg
│ ├── age_12plus.svg
│ ├── age_17plus.svg
├── files/ # 存储 App 文件
```
## 环境要求
- PHP 7.4+
- MySQL 5.7+
- Composer
- Node.js (可选,用于前端资源构建)
- Web 服务器(如 Apache 或 Nginx
## 快速启动指南
对于有经验的开发者,可按照以下步骤快速部署:
```cmd
# 1. 克隆项目并进入目录
.git clone <repository-url> app2
cd app2
# 2. 创建并配置环境文件
copy config.example.php config.php
# 编辑config.php设置数据库和邮件信息
# 3. 创建数据库并导入结构
mysql -u root -p -e "CREATE DATABASE your_db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -u root -p your_db_name < app_store.sql
# 4. 安装依赖并设置权限
composer install
icacls files /grant Users:(OI)(CI)W
icacls images /grant Users:(OI)(CI)W
# 5. 启动开发服务器
php -S localhost:8000
```
访问 http://localhost:8000 开始使用管理员后台地址http://localhost:8000/admin
## 详细安装教程
### 1. 环境准备
确保您的系统满足以下要求:
- PHP 7.4+推荐PHP 8.0+
- MySQL 5.7+ 或 MariaDB 10.2+
- ComposerPHP依赖管理工具
- Web服务器Apache/Nginx/IIS或PHP内置服务器
- Git可选用于版本控制
#### 检查PHP环境
打开命令提示符输入以下命令验证PHP版本
```cmd
php -v
# 应显示PHP 7.4.0或更高版本
# 检查必要扩展
php -m | findstr /i "mysqli pdo_mysql json curl fileinfo"
# 确保以上扩展均已安装
```
### 2. 获取项目代码
选择以下任一方式获取代码:
#### 方式一使用Git克隆推荐
```cmd
git clone <repository-url> app2
cd app2
```
#### 方式二:手动下载
1. 从项目仓库下载ZIP压缩包
2. 解压到本地目录(如 `c:\web\app2`
3. 打开命令提示符,进入项目目录:
```cmd
cd c:\web\app2
```
### 3. 数据库配置
#### 创建数据库
1. 登录MySQL控制台
```cmd
mysql -u root -p
```
2. 创建数据库(将`your_db_name`替换为您喜欢的数据库名称):
```sql
CREATE DATABASE your_db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
exit
```
#### 导入数据库结构
```cmd
mysql -u root -p your_db_name < app_store.sql
```
> **注意**:请确保在导入前替换命令中的`your_db_name`为您实际创建的数据库名称
### 4. 应用配置
#### 创建配置文件
如果项目中存在`config.example.php`
```cmd
copy config.example.php config.php
```
如果不存在,请手动创建`config.php`文件并添加以下内容:
```php
<?php
// 数据库配置
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', 'your_database_password');
define('DB_NAME', 'your_db_name');
define('APP_URL', 'http://localhost'); // 应用基础URL
define('ADMIN_EMAIL', 'admin@example.com'); // 默认管理员邮箱
define('ADMIN_PASSWORD', 'admin123'); // 默认管理员密码(首次登录后必须修改)
// 邮件服务配置(用于开发者注册验证)
define('SMTP_HOST', 'smtp.example.com');
define('SMTP_PORT', 465);
define('SMTP_USER', 'your_email@example.com');
define('SMTP_PASS', 'your_email_password');
define('SMTP_ENCRYPTION', 'ssl'); // 通常为ssl或tls
define('DEBUG_MODE', true); // 开发环境设为true生产环境设为false
?>
```
#### 配置参数说明
| 参数 | 说明 | 示例值 |
|------|------|--------|
| DB_HOST | 数据库主机地址 | localhost |
| DB_USER | 数据库用户名 | root |
| DB_PASS | 数据库密码 | your_actual_password |
| DB_NAME | 数据库名称 | app_store |
| APP_URL | 应用访问URL | http://localhost/app2 |
| DEBUG_MODE | 调试模式开关 | true/false |
### 5. 安装依赖
使用Composer安装项目依赖
```cmd
composer install
```
> 如果没有安装Composer请先从 https://getcomposer.org/ 下载并安装
### 6. 设置目录权限
项目需要对以下目录有写入权限:
- `files/`:存储上传的应用文件
- `images/`:存储应用截图和图标
#### 图形界面方式(推荐)
1. 在文件资源管理器中找到项目目录
2. 右键点击`files`文件夹,选择**属性**
3. 切换到**安全**选项卡,点击**编辑**
4. 选择当前用户,勾选**写入**权限,点击**确定**
5. 对`images`文件夹执行相同操作
#### 命令行方式
```cmd
icacls files /grant Users:(OI)(CI)W
icacls images /grant Users:(OI)(CI)W
```
### 7. 配置Web服务器
#### 选项A使用PHP内置开发服务器推荐用于开发
```cmd
php -S localhost:8000
```
## API 文档
### 根路径 `/api`
- **方法**GET
- **功能**:返回可用端点信息
- **响应示例**
```json
{
"status": "success",
"message": "App Store API",
"version": "1.0",
"endpoints": {
"/api?action=list": "获取应用列表支持search、platform、age_rating、tag、page、limit参数。search搜索关键词platform平台age_rating年龄分级tag标签page页码limit每页数量",
"/api?action=app&id=1": "获取指定ID的应用详情需传入app_id参数。包含应用基础信息、版本、图片、评价和标签信息",
},
"example": "GET /api?action=list&search=游戏&limit=10"
}
```
### 应用列表 `/api?action=list`
- **方法**GET
- **功能**:获取应用列表,支持多条件筛选和分页查询
- **参数**
- `search`:搜索关键词,可选
- `platform`:平台,可选
- `age_rating`:年龄分级,可选
- `tag`:标签,可选
- `page`页码可选默认1
- `limit`每页数量可选默认10
- **响应示例**
```json
{
"data": [
{
"id": "1",
"name": "示例应用",
"description": "这是一个示例应用",
"age_rating": "3+",
"avg_rating": "4.5"
}
],
"pagination": {
"total": 100,
"page": 1,
"limit": 10,
"totalPages": 10
}
}
```
### 应用详情 `/api?action=app&id={id}`
- **方法**GET
- **功能**获取指定ID应用的详细信息包含基础信息、版本、图片、评价和标签信息
- **参数**
- `id`应用ID必需数字类型
- **响应示例**:响应包含应用基础信息、版本、图片、评价和标签信息。

381
admin/addapp.php Normal file
View File

@@ -0,0 +1,381 @@
<?php
require_once '../config.php';
session_start();
// 检查是否已登录
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit();
}
// 非全部权限管理员重定向到对应权限页面
if ($_SESSION['admin']['permission'] != 'all') {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
$success = '';
$error = '';
// 处理添加App请求
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_app'])) {
$name = $_POST['name'];
$description = $_POST['description'];
$ageRating = $_POST['age_rating'];
$platforms = isset($_POST['platforms']) ? json_encode($_POST['platforms']) : json_encode([]);
// 处理表单提交
// 验证必填字段
$required = ['name', 'description', 'age_rating', 'platforms'];
$errors = [];
foreach ($required as $field) {
if (empty($_POST[$field])) {
$errors[] = ucfirst($field) . ' 不能为空';
}
}
// 年龄分级说明验证
if (($_POST['age_rating'] === '12+' || $_POST['age_rating'] === '17+') && empty($_POST['age_rating_description'])) {
$errors[] = '年龄分级为12+或以上时,年龄分级说明不能为空';
}
// 处理应用图标上传
// 处理平台数据
$platforms = json_encode($_POST['platforms']);
// 插入应用数据
$stmt = $conn->prepare("INSERT INTO apps (name, description, age_rating, age_rating_description, platforms) VALUES (?, ?, ?, ?, ?)");
if (!$stmt) {
$error = "Database error: " . $conn->error;
}
if ($stmt) {
$stmt->bind_param("sssss", $name, $description, $ageRating, $_POST['age_rating_description'], $platforms);
if ($stmt->execute() === TRUE) {
$appId = $stmt->insert_id;
// 保存标签关联
if (!empty($_POST['tags'])) {
$stmt = $conn->prepare("INSERT INTO app_tags (app_id, tag_id) VALUES (?, ?)");
foreach ($_POST['tags'] as $tagId) {
$stmt->bind_param("ii", $appId, $tagId);
$stmt->execute();
}
$stmt->close();
}
// 处理上传的预览图片
if (!empty($_FILES['images']['name'][0])) {
$uploadDir = '../images/';
foreach ($_FILES['images']['tmp_name'] as $key => $tmpName) {
$fileName = basename($_FILES['images']['name'][$key]);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($tmpName, $targetPath)) {
$insertImageSql = "INSERT INTO app_images (app_id, image_path) VALUES (?, ?)";
$imgStmt = $conn->prepare($insertImageSql);
$imgStmt->bind_param("is", $appId, $targetPath);
$imgStmt->execute();
}
}
}
// 处理上传的App文件
if (!empty($_FILES['app_file']['name'])) {
$uploadDir = '../files/';
$fileName = basename($_FILES['app_file']['name']);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
$version = $_POST['version'];
$changelog = $_POST['changelog'];
$insertVersionSql = "INSERT INTO app_versions (app_id, version, changelog, file_path) VALUES (?, ?, ?, ?)";
$verStmt = $conn->prepare($insertVersionSql);
$verStmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
$verStmt->execute();
}
}
echo '<script>Swal.fire("成功", "App 添加成功", "success").then(() => { window.location.href = "index.php"; });</script>';
exit;
} else {
echo '<script>Swal.fire("错误", "App 添加失败: '. $conn->error .'", "error");</script>';
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>添加App - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<script src="/js/sweetalert.js"></script>
</head>
<body>
<div class="admin-header">
<h1>应用管理系统</h1>
<div class="admin-actions">
<span>欢迎, <?php echo htmlspecialchars($_SESSION['admin']['username']); ?></span>
<a href="#" onclick="confirmLogout()" class="logout-btn">登出</a>
</div>
</div>
<script>
function confirmLogout() {
Swal.fire({
title: '确定要登出吗?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'logout.php';
}
});
}
</script>
<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>添加App - <?php echo APP_STORE_NAME; ?></title>\n <!-- Bootstrap CSS -->\n <link href="../css/bootstrap.min.css" rel="stylesheet">\n <!-- 自定义CSS -->\n <link rel="stylesheet" href="../styles.css">\n <script src="/js/sweetalert.js"></script>\n <script>\n function confirmLogout() {\n Swal.fire({\n title: '确定要登出吗?',\n icon: 'question',\n showCancelButton: true,\n confirmButtonText: '确定',\n cancelButtonText: '取消'\n }).then((result) => {\n if (result.isConfirmed) {\n window.location.href = 'logout.php';\n }\n });\n }\n </script>\n <!-- Fluent Design 模糊效果 -->\n <style>\n .blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>
</head>
<body class="page-transition">
<?php if (isset($error)): ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
<div style='color: red; padding: 10px; background-color: #ffeeee; border-radius: 5px; margin-bottom: 20px;'>
<?php echo htmlspecialchars($error); ?>
</div>
<?php endif; ?>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">App列表</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="addapp.php">添加App</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="confirmLogout()">退出登录</a>\n </li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<?php if (!empty($success)): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<h2>添加App</h2>
<form method="post" enctype="multipart/form-data">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="name" name="name" required>
<label for="name"><i class="fas fa-file-signature me-2"></i>App名称</label>
</div>
<div class="mb-3">
<label for="description" class="form-label"><i class="fas fa-align-left me-2"></i>描述</label>
<textarea class="form-control" id="description" name="description" rows="3" required></textarea>
</div>
<div class="mb-3">
<label for="age_rating" class="form-label"><i class="fas fa-shield-alt me-2"></i>年龄分级</label>
<select class="form-select" id="age_rating" name="age_rating" required>
<option value="3+">3+</option>
<option value="7+">7+</option>
<option value="12+">12+</option>
<option value="17+">17+</option>
</select>
</div>
<div class="form-floating mb-3" id="ageRatingDescriptionGroup" style="display: none;">
<textarea class="form-control" id="age_rating_description" name="age_rating_description" rows="3" placeholder="请说明为何需要此年龄分级"></textarea>
<label for="age_rating_description">年龄分级说明</label>
<div class="form-text">当年龄分级为12+或以上时,此项为必填</div>
</div>
<div class="mb-3">
<label class="form-label">适用平台</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="android" id="android" name="platforms[]">
<label class="form-check-label" for="android">Android</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="ios" id="ios" name="platforms[]">
<label class="form-check-label" for="ios">iOS</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="windows" id="windows" name="platforms[]">
<label class="form-check-label" for="windows">Windows</label>
</div>
<div id="windows_suboptions" class="ms-4 mt-2" style="display: none;">
<div class="form-check">
<input class="form-check-input" type="radio" name="windows_version" id="windows_xp" value="windows_xp">
<label class="form-check-label" for="windows_xp">XP以前</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="windows_version" id="windows_win7" value="windows_win7">
<label class="form-check-label" for="windows_win7">Win7以后</label>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="macos" id="macos" name="platforms[]">
<label class="form-check-label" for="macos">macOS</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="linux" id="linux" name="platforms[]">
<label class="form-check-label" for="linux">Linux</label>
</div>
<div id="linux_suboptions" class="ms-4 mt-2" style="display: none;">
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_ubuntu" value="linux_ubuntu">
<label class="form-check-label" for="linux_ubuntu">Ubuntu</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_arch" value="linux_arch">
<label class="form-check-label" for="linux_arch">Arch Linux</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_centos" value="linux_centos">
<label class="form-check-label" for="linux_centos">CentOS</label>
</div>
</div>
</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="version" name="version" required>
<label for="version" class="form-label"><i class="fas fa-code-branch me-2"></i>版本号</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="changelog" name="changelog" rows="3" required></textarea>
<label for="changelog" class="form-label"><i class="fas fa-history me-2"></i>更新日志</label>
</div>
<div class="form-floating mb-3">
<input class="form-control" type="file" id="app_file" name="app_file" required>
<label for="app_file" class="form-label"><i class="fas fa-file-archive me-2"></i>App文件</label>
</div>
<div class="form-floating mb-3">
<input class="form-control" type="file" id="images" name="images[]" multiple>
<label for="images" class="form-label"><i class="fas fa-upload me-2"></i>预览图片 (可多选)</label>
</div>
<button type="submit" name="add_app" class="btn btn-primary"><i class="fas fa-plus-circle me-2"></i>添加App</button>
<a href="index.php" class="btn btn-secondary ms-2"><i class="fas fa-times me-1"></i>取消</a>
</form>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
// 年龄分级说明显示控制
const ageRatingSelect = document.getElementById('age_rating');
const descriptionGroup = document.getElementById('ageRatingDescriptionGroup');
const descriptionInput = document.getElementById('age_rating_description');
function toggleAgeDescription() {
const selectedRating = ageRatingSelect.value;
if (selectedRating === '12+' || selectedRating === '17+') {
descriptionGroup.style.display = 'block';
descriptionInput.required = true;
} else {
descriptionGroup.style.display = 'none';
descriptionInput.required = false;
}
}
ageRatingSelect.addEventListener('change', toggleAgeDescription);
// 初始加载时检查
toggleAgeDescription();
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
// 平台子选项显示控制
document.getElementById('windows').addEventListener('change', function() {
const suboptions = document.getElementById('windows_suboptions');
suboptions.style.display = this.checked ? 'block' : 'none';
if (!this.checked) {
document.querySelectorAll('input[name="windows_version"]').forEach(radio => radio.checked = false);
}
});
document.getElementById('linux').addEventListener('change', function() {
const suboptions = document.getElementById('linux_suboptions');
suboptions.style.display = this.checked ? 'block' : 'none';
if (!this.checked) {
document.querySelectorAll('input[name="linux_distribution"]').forEach(radio => radio.checked = false);
}
});
// 表单提交验证
document.querySelector('form').addEventListener('submit', function(e) {
// 验证Windows子选项
if (document.getElementById('windows').checked && !document.querySelector('input[name="windows_version"]:checked')) {
e.preventDefault();
Swal.fire({
title: '提示',
text: '请选择Windows版本XP以前或Win7以后',
icon: 'warning',
confirmButtonText: '确定'
});
return;
}
// 验证Linux子选项
if (document.getElementById('linux').checked && !document.querySelector('input[name="linux_distribution"]:checked')) {
e.preventDefault();
Swal.fire({
title: '提示',
text: '请选择Linux发行版Ubuntu、Arch Linux或CentOS',
icon: 'warning',
confirmButtonText: '确定'
});
return;
}
// 更新平台值包含子选项信息
if (document.getElementById('windows').checked) {
document.getElementById('windows').value = document.querySelector('input[name="windows_version"]:checked').value;
}
if (document.getElementById('linux').checked) {
document.getElementById('linux').value = document.querySelector('input[name="linux_distribution"]:checked').value;
}
});
</script>
</body>
</html>

225
admin/announcements.php Normal file
View File

@@ -0,0 +1,225 @@
<?php
require_once '../config.php';
session_start();
// 检查管理员登录状态
if (!isset($_SESSION['admin']) || !isset($_SESSION['admin']['id'])) {
header('Location: login.php');
exit;
}
// 检查权限 - 允许all和say权限
if (!in_array($_SESSION['admin']['permission'], ['all', 'say'])) {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
// 处理公告发布
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$title = $_POST['title'] ?? '';
$content = $_POST['content'] ?? '';
$admin_id = $_SESSION['admin']['id'];
if (!empty($title) && !empty($content)) {
$stmt = $conn->prepare('INSERT INTO announcements (title, content, admin_id) VALUES (?, ?, ?)');
$stmt->bind_param('ssi', $title, $content, $admin_id);
if ($stmt->execute()) {
echo '<script>Swal.fire("成功", "公告发布成功", "success").then(() => { window.location.reload(); });</script>';
exit;
} else {
echo '<script>Swal.fire("错误", "公告发布失败: ' . $conn->error . '", "error");</script>';
}
$stmt->close();
} else {
echo '<script>Swal.fire("错误", "标题和内容不能为空", "error");</script>';
}
}
// 获取公告列表
$sql = 'SELECT a.*, ad.username FROM announcements a JOIN admins ad ON a.admin_id = ad.id ORDER BY a.created_at DESC';
$result = $conn->query($sql);
?>
<!DOCTYPE html>
<style>
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>公告管理 - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- SweetAlert2 CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
</style>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">App列表</a>
</li>
<li class="nav-item">
<a class="nav-link" href="addapp.php">添加App</a>
</li>
<li class="nav-item">
<a class="nav-link" href="review_apps.php">审核APP</a>
</li>
<li class="nav-item">
<a class="nav-link" href="manage_developers.php">管理开发者</a>
</li>
<li class="nav-item">
<a class="nav-link" href="system_info.php">系统信息</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="announcements.php">公告管理</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="confirmLogout()">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<script>
function confirmLogout() {
Swal.fire({
title: '确定要登出吗?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'logout.php';
}
});
}
function confirmDelete(id) {
Swal.fire({
title: '确定要删除这条公告吗?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'delete_announcement.php?id=' + id;
}
});
}
</script>
<?php if (isset($_GET['success'])): ?>
<script>
Swal.fire({
icon: "success",
title: "成功",
text: "<?php echo addslashes($_GET['success']); ?>",
});
</script>
<?php endif; ?>
<?php if (isset($error)): ?>
<script>
Swal.fire({
icon: "error",
title: "错误",
text: "<?php echo addslashes($error); ?>",
});
</script>
<?php endif; ?>
<h2><i class="fas fa-bullhorn me-2"></i>发布公告</h2>
<form method="post">
<div class="mb-3">
<label for="title" class="form-label"><i class="fas fa-heading me-2"></i>标题</label>
<input type="text" class="form-control" id="title" name="title" required>
</div>
<div class="mb-3">
<label for="content" class="form-label"><i class="fas fa-paragraph me-2"></i>内容</label>
<textarea class="form-control" id="content" name="content" rows="4" required></textarea>
</div>
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane me-2"></i>发布</button>
</form>
<h2 class="mt-4"><i class="fas fa-list-alt me-2"></i>公告列表</h2>
<table class="table table-striped">
<thead>
<tr>
<th><i class="fas fa-id-card me-2"></i>ID</th>
<th><i class="fas fa-heading me-2"></i>标题</th>
<th><i class="fas fa-user me-2"></i>发布者</th>
<th><i class="fas fa-clock me-2"></i>发布时间</th>
<th><i class="fas fa-cog me-2"></i>操作</th>
</tr>
</thead>
<tbody>
<?php while ($row = $result->fetch_assoc()): ?>
<tr>
<td><?php echo $row['id']; ?></td>
<td><?php echo htmlspecialchars($row['title']); ?></td>
<td><?php echo htmlspecialchars($row['username']); ?></td>
<td><?php echo $row['created_at']; ?></td>
<td>
<button class="btn btn-danger btn-sm" onclick="confirmDelete(<?php echo $row['id']; ?>)"><i class="fas fa-trash-alt me-1"></i>删除</button>
</td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<!-- SweetAlert2 JS -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js"></script>
<script>
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
</body>
</html>

89
admin/debug_redirect.php Normal file
View File

@@ -0,0 +1,89 @@
<?php
// 设置错误报告级别以查看详细错误
error_reporting(E_ALL);
ini_set('display_errors', 1);
// 记录重定向链
session_start();
if (!isset($_SESSION['redirect_chain'])) {
$_SESSION['redirect_chain'] = [];
}
// 获取当前文件名
$current_file = basename($_SERVER['PHP_SELF']);
// 添加到重定向链
if (!in_array($current_file, $_SESSION['redirect_chain'])) {
$_SESSION['redirect_chain'][] = $current_file;
}
// 如果重定向链太长,说明存在循环重定向
if (count($_SESSION['redirect_chain']) > 5) {
echo '<!DOCTYPE html><html><head><title>循环重定向检测</title></head><body>';
echo '<h1>检测到循环重定向!</h1>';
echo '<p>重定向链:' . implode(' -> ', $_SESSION['redirect_chain']) . '</p>';
echo '<p>请查看以下详细信息来排查问题:</p>';
// 输出会话信息
echo '<h2>会话信息</h2>';
echo '<pre>';
print_r($_SESSION);
echo '</pre>';
// 尝试加载配置文件
echo '<h2>配置文件检测</h2>';
if (file_exists('../config.php')) {
echo 'config.php 文件存在<br>';
// 尝试加载配置但捕获错误
try {
require_once '../config.php';
echo '配置文件加载成功<br>';
// 检查管理员账户配置
echo '<h3>管理员账户配置</h3>';
if (isset($admin_accounts) && is_array($admin_accounts)) {
echo '找到 ' . count($admin_accounts) . ' 个管理员账户<br>';
foreach ($admin_accounts as $account) {
echo '用户名: ' . $account['username'] . ', 权限: ' . $account['permission'] . '<br>';
}
} else {
echo '未找到管理员账户配置<br>';
}
// 检查数据库连接
echo '<h3>数据库连接</h3>';
if (isset($conn) && $conn instanceof mysqli) {
echo '数据库连接对象存在<br>';
if ($conn->ping()) {
echo '数据库连接成功<br>';
} else {
echo '数据库连接失败: ' . $conn->error . '<br>';
}
} else {
echo '数据库连接对象不存在<br>';
}
} catch (Exception $e) {
echo '配置文件加载错误: ' . $e->getMessage() . '<br>';
}
} else {
echo 'config.php 文件不存在<br>';
}
// 重置重定向链
echo '<h2>修复选项</h2>';
echo '<a href="?reset=1">重置重定向链</a>';
if (isset($_GET['reset']) && $_GET['reset'] == 1) {
unset($_SESSION['redirect_chain']);
echo '<script>alert("重定向链已重置"); window.location.href = "login.php";</script>';
}
echo '</body></html>';
exit;
}
// 正常情况:重定向到登录页面进行测试
echo '<!DOCTYPE html><html><head><title>重定向调试</title></head><body>';
echo '<p>正在重定向到登录页面进行测试...</p>';
echo '<script>setTimeout(function(){ window.location.href = "login.php"; }, 1000);</script>';
echo '</body></html>';

View File

@@ -0,0 +1,41 @@
<?php
require_once '../config.php';
session_start();
// 检查管理员登录状态
if (!isset($_SESSION['admin']) || !isset($_SESSION['admin']['id'])) {
header('Location: login.php');
exit;
}
// 检查权限 - 只允许all权限
if ($_SESSION['admin']['permission'] !== 'all') {
header('Location: announcements.php?error=没有删除公告的权限');
exit();
}
// 验证公告ID
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header('Location: announcements.php?error=无效的公告ID');
exit();
}
$announcement_id = intval($_GET['id']);
// 执行删除操作
$stmt = $conn->prepare('DELETE FROM announcements WHERE id = ?');
$stmt->bind_param('i', $announcement_id);
if ($stmt->execute()) {
$success = '公告已成功删除';
} else {
$error = '删除公告失败: ' . $conn->error;
}
$stmt->close();
$conn->close();
// 重定向回公告管理页面并显示结果
$redirect = 'announcements.php?' . ($success ? 'success=' . urlencode($success) : 'error=' . urlencode($error));
header('Location: ' . $redirect);
exit;

41
admin/deleteapp.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
require_once '../config.php';
session_start();
// 检查管理员登录状态
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit;
}
// 验证App ID
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
echo '<script>Swal.fire("错误", "无效的App ID", "error").then(() => { window.location.href = "index.php"; });</script>';
exit;
}
$appId = $_GET['id'];
// 删除App
$deleteAppSql = "DELETE FROM apps WHERE id = ?";
$stmt = $conn->prepare($deleteAppSql);
$stmt->bind_param("i", $appId);
if ($stmt->execute() === TRUE) {
// 删除关联的图片
$deleteImagesSql = "DELETE FROM app_images WHERE app_id = ?";
$imgStmt = $conn->prepare($deleteImagesSql);
$imgStmt->bind_param("i", $appId);
$imgStmt->execute();
// 删除关联的版本
$deleteVersionsSql = "DELETE FROM app_versions WHERE app_id = ?";
$verStmt = $conn->prepare($deleteVersionsSql);
$verStmt->bind_param("i", $appId);
$verStmt->execute();
echo '<script>Swal.fire("成功", "App 删除成功", "success").then(() => { window.location.href = "index.php"; });</script>';
} else {
echo '<script>Swal.fire("错误", "App 删除失败: '. $conn->error .'", "error").then(() => { window.location.href = "index.php"; });</script>';
}
exit;
?>

402
admin/editapp.php Normal file
View File

@@ -0,0 +1,402 @@
<?php
require_once '../config.php';
session_start();
// 检查是否已登录
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit();
}
// 非全部权限管理员重定向到对应权限页面
if ($_SESSION['admin']['permission'] != 'all') {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
// 验证App ID
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
echo '<script>Swal.fire("错误", "无效的App ID", "error").then(() => { window.location.href = "index.php"; });</script>';
exit;
}
$appId = $_GET['id'];
// 获取App信息
$app = null;
$getAppSql = "SELECT * FROM apps WHERE id = ?";
$stmt = $conn->prepare($getAppSql);
$stmt->bind_param("i", $appId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
echo '<script>Swal.fire("错误", "App不存在", "error").then(() => { window.location.href = "index.php"; });</script>';
exit;
}
$app = $result->fetch_assoc();
$platforms = json_decode($app['platforms'], true);
$success = '';
$error = '';
// 处理编辑App请求
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_app'])) {
$name = $_POST['name'];
$description = $_POST['description'];
$ageRating = $_POST['age_rating'];
$newPlatforms = json_encode($_POST['platforms'] ?? []);
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 验证必填字段
$required = ['name', 'description', 'age_rating', 'platforms'];
$errors = [];
foreach ($required as $field) {
if (empty($_POST[$field])) {
$errors[] = ucfirst($field) . ' 不能为空';
}
}
// 年龄分级验证
if (($_POST['age_rating'] === '12+' || $_POST['age_rating'] === '17+') && empty($_POST['age_rating_description'])) {
$errors[] = '年龄分级为12+或以上时,年龄分级说明不能为空';
}
// 处理应用图标上传(如果有新上传)
if (!empty($_FILES['images']['name'][0])) {
$uploadDir = '../images/';
foreach ($_FILES['images']['tmp_name'] as $key => $tmpName) {
$fileName = basename($_FILES['images']['name'][$key]);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($tmpName, $targetPath)) {
$insertImageSql = "INSERT INTO app_images (app_id, image_path) VALUES (?, ?)";
$imgStmt = $conn->prepare($insertImageSql);
$imgStmt->bind_param("is", $appId, $targetPath);
$imgStmt->execute();
}
}
}
// 处理新上传的App文件
if (!empty($_FILES['app_file']['name'])) {
$uploadDir = '../files/';
$fileName = basename($_FILES['app_file']['name']);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
$version = $_POST['version'];
$changelog = $_POST['changelog'];
$insertVersionSql = "INSERT INTO app_versions (app_id, version, changelog, file_path) VALUES (?, ?, ?, ?)";
$verStmt = $conn->prepare($insertVersionSql);
$verStmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
$verStmt->execute();
}
}
// 更新标签关联
$stmt = $conn->prepare("DELETE FROM app_tags WHERE app_id = ?");
$stmt->bind_param("i", $appId);
$stmt->execute();
$stmt->close();
if (!empty($_POST['tags'])) {
$stmt = $conn->prepare("INSERT INTO app_tags (app_id, tag_id) VALUES (?, ?)");
foreach ($_POST['tags'] as $tagId) {
$stmt->bind_param("ii", $appId, $tagId);
$stmt->execute();
}
$stmt->close();
}
$updateAppSql = "UPDATE apps SET name = ?, description = ?, age_rating = ?, platforms = ? WHERE id = ?";
$updateStmt = $conn->prepare($updateAppSql);
$updateStmt->bind_param("ssssi", $name, $description, $ageRating, $newPlatforms, $appId);
if ($updateStmt->execute() === TRUE) {
echo '<script>Swal.fire("成功", "App 更新成功", "success").then(() => { window.location.href = "index.php"; });</script>';
exit;
} else {
echo '<script>Swal.fire("错误", "App 更新失败: '. $conn->error .'", "error");</script>';
}
$updateStmt->close();
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>编辑App - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<script src="/js/sweetalert.js"></script>
<script>
function confirmLogout() {
Swal.fire({
title: '确定要登出吗?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'logout.php';
}
});
}
</script>
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">App列表</a>
</li>
<li class="nav-item">
<a class="nav-link" href="addapp.php">添加App</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="editapp.php?id=<?php echo $appId; ?>">编辑App</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="confirmLogout()">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<?php if (!empty($success)): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<h2>编辑App: <?php echo htmlspecialchars($app['name']); ?></h2>
<form method="post" enctype="multipart/form-data">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($app['name']); ?>" required>
<label for="name"><i class="fas fa-file-signature me-2"></i>App名称</label>
</div>
<div class="mb-3">
<label for="description" class="form-label"><i class="fas fa-align-left me-2"></i>描述</label>
<textarea class="form-control" id="description" name="description" rows="3" required><?php echo htmlspecialchars($app['description']); ?></textarea>
</div>
<div class="mb-3">
<label for="age_rating" class="form-label"><i class="fas fa-shield-alt me-2"></i>年龄分级</label>
<select class="form-select" id="age_rating" name="age_rating" required>
<option value="3+" <?php echo $app['age_rating'] === '3+' ? 'selected' : ''; ?>>3+</option>
<option value="7+" <?php echo $app['age_rating'] === '7+' ? 'selected' : ''; ?>>7+</option>
<option value="12+" <?php echo $app['age_rating'] === '12+' ? 'selected' : ''; ?>>12+</option>
<option value="17+" <?php echo $app['age_rating'] === '17+' ? 'selected' : ''; ?>>17+</option>
</select>
</div>
<div class="mb-3" id="ageRatingDescriptionGroup" style="display: none;">
<label for="age_rating_description" class="form-label">年龄分级说明</label>
<textarea class="form-control" id="age_rating_description" name="age_rating_description" rows="3" placeholder="请说明为何需要此年龄分级"><?php echo htmlspecialchars($app['age_rating_description'] ?? ''); ?></textarea>
<div class="form-text">当年龄分级为12+或以上时,此项为必填</div>
</div>
<div class="mb-3">
<label class="form-label">适用平台</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="android" id="android" name="platforms[]" <?php echo in_array('android', $platforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="android">Android</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="ios" id="ios" name="platforms[]" <?php echo in_array('ios', $platforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="ios">iOS</label>
</div>
<?php
$windowsChecked = false;
$windowsVersion = '';
foreach ($platforms as $p) {
if (strpos($p, 'windows_') === 0) {
$windowsChecked = true;
$windowsVersion = $p;
break;
}
}
?>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="windows" id="windows" name="platforms[]" <?php echo $windowsChecked ? 'checked' : ''; ?>>
<label class="form-check-label" for="windows">Windows</label>
</div>
<div id="windows_suboptions" class="ms-4 mt-2" style="display: <?php echo $windowsChecked ? 'block' : 'none'; ?>">
<div class="form-check">
<input class="form-check-input" type="radio" name="windows_version" id="windows_xp" value="windows_xp" <?php echo $windowsVersion === 'windows_xp' ? 'checked' : ''; ?>>
<label class="form-check-label" for="windows_xp">XP以前</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="windows_version" id="windows_win7" value="windows_win7" <?php echo $windowsVersion === 'windows_win7' ? 'checked' : ''; ?>>
<label class="form-check-label" for="windows_win7">Win7以后</label>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="macos" id="macos" name="platforms[]" <?php echo in_array('macos', $platforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="macos">macOS</label>
</div>
<?php
$linuxChecked = false;
$linuxVersion = '';
foreach ($platforms as $p) {
if (strpos($p, 'linux_') === 0) {
$linuxChecked = true;
$linuxVersion = $p;
break;
}
}
?>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="linux" id="linux" name="platforms[]" <?php echo $linuxChecked ? 'checked' : ''; ?>>
<label class="form-check-label" for="linux">Linux</label>
</div>
<div id="linux_suboptions" class="ms-4 mt-2" style="display: <?php echo $linuxChecked ? 'block' : 'none'; ?>">
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_ubuntu" value="linux_ubuntu" <?php echo $linuxVersion === 'linux_ubuntu' ? 'checked' : ''; ?>>
<label class="form-check-label" for="linux_ubuntu">Ubuntu</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_arch" value="linux_arch" <?php echo $linuxVersion === 'linux_arch' ? 'checked' : ''; ?>>
<label class="form-check-label" for="linux_arch">Arch Linux</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_centos" value="linux_centos" <?php echo $linuxVersion === 'linux_centos' ? 'checked' : ''; ?>>
<label class="form-check-label" for="linux_centos">CentOS</label>
</div>
</div>
</div>
<div class="form-floating mb-3">
<input type="text" class="form-control" id="version" name="version" placeholder="如: 1.0.1">
<label for="version" class="form-label"><i class="fas fa-code-branch me-2"></i>版本号</label>
<div class="form-text">仅在上传新安装包时填写</div>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="changelog" name="changelog" rows="3" placeholder="描述本次更新内容"></textarea>
<label for="changelog" class="form-label"><i class="fas fa-history me-2"></i>更新日志</label>
</div>
<div class="mb-3">
<label for="app_file" class="form-label">新App文件 (可选)</label>
<input class="form-control" type="file" id="app_file" name="app_file">
</div>
<div class="mb-3">
<label for="images" class="form-label"><i class="fas fa-upload me-2"></i>预览图片 (可多选)</label>
<input class="form-control" type="file" id="images" name="images[]" multiple>
</div>
<button type="submit" name="edit_app" class="btn btn-primary"><i class="fas fa-save me-2"></i>保存修改</button>
<a href="index.php" class="btn btn-secondary ms-2"><i class="fas fa-times me-1"></i>取消</a>
</form>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
// 年龄分级说明显示控制
// 年龄分级说明显示控制
const ageRatingSelect = document.getElementById('age_rating');
const descriptionGroup = document.getElementById('ageRatingDescriptionGroup');
const descriptionInput = document.getElementById('age_rating_description');
function toggleAgeDescription() {
const selectedRating = ageRatingSelect.value;
if (selectedRating === '12+' || selectedRating === '17+') {
descriptionGroup.style.display = 'block';
descriptionInput.required = true;
} else {
descriptionGroup.style.display = 'none';
descriptionInput.required = false;
}
}
ageRatingSelect.addEventListener('change', toggleAgeDescription);
// 初始加载时检查
toggleAgeDescription();
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
// 平台子选项显示控制
document.getElementById('windows').addEventListener('change', function() {
const suboptions = document.getElementById('windows_suboptions');
suboptions.style.display = this.checked ? 'block' : 'none';
if (!this.checked) {
document.querySelectorAll('input[name="windows_version"]').forEach(radio => radio.checked = false);
}
});
document.getElementById('linux').addEventListener('change', function() {
const suboptions = document.getElementById('linux_suboptions');
suboptions.style.display = this.checked ? 'block' : 'none';
if (!this.checked) {
document.querySelectorAll('input[name="linux_distribution"]').forEach(radio => radio.checked = false);
}
});
// 表单提交验证
document.querySelector('form').addEventListener('submit', function(e) {
// 验证Windows子选项
if (document.getElementById('windows').checked && !document.querySelector('input[name="windows_version"]:checked')) {
e.preventDefault();
Swal.fire({
title: '提示',
text: '请选择Windows版本XP以前或Win7以后',
icon: 'warning',
confirmButtonText: '确定'
});
return;
}
// 验证Linux子选项
if (document.getElementById('linux').checked && !document.querySelector('input[name="linux_distribution"]:checked')) {
e.preventDefault();
Swal.fire({
title: '提示',
text: '请选择Linux发行版Ubuntu、Arch Linux或CentOS',
icon: 'warning',
confirmButtonText: '确定'
});
return;
}
// 更新平台值包含子选项信息
if (document.getElementById('windows').checked) {
document.getElementById('windows').value = document.querySelector('input[name="windows_version"]:checked').value;
}
if (document.getElementById('linux').checked) {
document.getElementById('linux').value = document.querySelector('input[name="linux_distribution"]:checked').value;
}
});
</script>
</body>
</html>
<?php
// 更新应用数据
$stmt = $conn->prepare("UPDATE apps SET name=?, description=?, age_rating=?, age_rating_description=?, platforms=?, updated_at=NOW() WHERE id=?");
$stmt->bind_param("sssssi", $name, $description, $age_rating, $_POST['age_rating_description'], $platformsJson, $appId);
?>

215
admin/index.php Normal file
View File

@@ -0,0 +1,215 @@
<?php
require_once '../config.php';
session_start();
if (!isset($conn) || !$conn instanceof mysqli) {
die('数据库连接失败,请检查配置文件。');
}
// 获取最新公告
$sql = 'SELECT title, content FROM announcements ORDER BY created_at DESC LIMIT 1';
$result = $conn->query($sql);
$announcement = $result ? $result->fetch_assoc() : null;
// 检查是否已登录
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit();
}
// 非全部权限管理员重定向到对应权限页面
if ($_SESSION['admin']['permission'] != 'all') {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
// 处理退出登录
if (isset($_GET['logout'])) {
unset($_SESSION['admin']);
header('Location: login.php');
exit;
}
// 获取App列表
$sqlApps = "SELECT * FROM apps WHERE status = 'approved' ORDER BY created_at DESC";
$resultApps = $conn->query($sqlApps);
if (!$resultApps) {
error_log("Database query failed: " . $conn->error);
echo '<script>Swal.fire("错误", "获取App列表失败请联系管理员。", "error");</script>';
} else {
?>
<!DOCTYPE html>
<style>
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>App管理 - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<script src="/js/sweetalert.js"></script>
<script>
function confirmLogout() {
Swal.fire({
title: '确定要登出吗?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'logout.php';
}
});
}
</script>
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
</style>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="index.php">App列表</a>
</li>
<li class="nav-item">
<a class="nav-link" href="addapp.php">添加App</a>
</li>
<li class="nav-item">
<a class="nav-link" href="review_apps.php">审核APP</a>
</li>
<li class="nav-item">
<a class="nav-link" href="manage_developers.php">管理开发者</a>
</li>
<li class="nav-item">
<a class="nav-link" href="system_info.php">系统信息</a>
</li>
<li class="nav-item">
<a class="nav-link" href="announcements.php">公告管理</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="confirmLogout()">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<script>
<?php if (isset($_GET['success'])): ?>
Swal.fire({
icon: "success",
title: "成功",
text: "<?php echo addslashes($_GET['success']); ?>",
});
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
Swal.fire({
icon: "error",
title: "错误",
text: "<?php echo addslashes($_GET['error']); ?>",
});
<?php endif; ?>
</script>
<h2>App列表</h2>
<div class="mb-3">
<a href="manage_tags.php" class="btn btn-info"><i class="fas fa-tags me-2"></i>标签管理</a>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>年龄分级</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php while ($app = $resultApps->fetch_assoc()): ?>
<tr>
<td><?php echo $app['id']; ?></td>
<td><?php echo htmlspecialchars($app['name']); ?></td>
<td><?php echo $app['age_rating']; ?></td>
<td><?php echo $app['created_at']; ?></td>
<td>
<a href="editapp.php?id=<?php echo $app['id']; ?>" class="btn btn-sm btn-outline-primary"><i class="fas fa-edit me-1"></i>编辑</a>
<a href="manage_versions.php?app_id=<?php echo $app['id']; ?>" class="btn btn-sm btn-outline-secondary"><i class="fas fa-code-branch me-1"></i>版本管理</a>
<a href="deleteapp.php?id=<?php echo $app['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="event.preventDefault(); Swal.fire({
title: '确定要删除吗?',
text: '删除后将无法恢复!',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: '确定删除'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = this.href;
}
});"><i class="fas fa-trash-alt me-1"></i>删除</a>
</td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
</body>
</html>
<?php
}
?>

165
admin/login.php Normal file
View File

@@ -0,0 +1,165 @@
<?php
require_once '../config.php';
// 检查管理员登录状态
session_start();
if (!isset($_SESSION['admin'])) {
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username']) && isset($_POST['password'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$adminFound = false;
foreach ($admin_accounts as $account) {
if ($username === $account['username'] && $password === $account['password']) {
$_SESSION['admin'] = [
'id' => $account['id'],
'username' => $account['username'],
'permission' => $account['permission']
];
$adminFound = true;
// 处理自动登录
if (isset($_POST['remember_me']) && $_POST['remember_me'] === 'on') {
$cookie_lifetime = 30 * 24 * 60 * 60; // 30天
$cookie_params = session_get_cookie_params();
setcookie(
session_name(),
session_id(),
time() + $cookie_lifetime,
$cookie_params['path'],
$cookie_params['domain'],
$cookie_params['secure'],
$cookie_params['httponly']
);
ini_set('session.gc_maxlifetime', $cookie_lifetime);
}
// 根据权限设置重定向页面
$redirectPage = 'index.php';
if ($_SESSION['admin']['permission'] == 'say') {
$redirectPage = 'announcements.php';
} elseif ($_SESSION['admin']['permission'] == 'review') {
$redirectPage = 'review_apps.php';
}
header("Location: $redirectPage");
exit();
}
}
if (!$adminFound) {
$error = '用户名或密码错误';
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理员登录 - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<!-- 顶栏样式 -->
<style>
.navbar.scrolled {
background-color: rgba(255, 255, 255, 0.95) !important;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>
<script src="/js/sweetalert.js"></script>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="../index.php">首页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="index.php">管理后台</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- 为内容添加顶部内边距 -->
<div style="padding-top: 70px;">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card blur-bg">
<div class="card-header"><i class="fas fa-sign-in-alt me-2"></i>管理员登录</div>
<div class="card-body">
<!-- <?php if (isset($error)): ?>
<script>
Swal.fire({
icon: "error",
title: "错误",
text: "<?php echo addslashes($error); ?>",
});
</script>
<?php endif; ?> -->
<form method="post">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" required>
<label for="username"><i class="fas fa-user me-2"></i>用户名</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" required>
<label for="password"><i class="fas fa-lock me-2"></i>密码</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="remember_me" id="remember_me">
<label class="form-check-label" for="remember_me">
<i class="fas fa-clock me-2"></i>自动登录
</label>
</div>
<button type="submit" class="btn btn-primary"><i class="fas fa-sign-in-alt me-2"></i>登录</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
</body>
</html>
<?php
exit;
}

37
admin/logout.php Normal file
View File

@@ -0,0 +1,37 @@
<?php
require_once '../config.php';
session_start();
// 销毁所有会话变量
$_SESSION = [];
// 如果使用了基于cookie的会话也需要删除cookie
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
// 销毁会话
session_destroy();
// 使用Sweet Alert弹窗提示并跳转登录页
echo '<script src="/js/sweetalert.js"></script>';
echo '<script>document.addEventListener("DOMContentLoaded", function() {
Swal.fire({
title: "登出成功",
text: "您已安全登出系统",
icon: "success",
timer: 1500,
showConfirmButton: false,
target: document.body
}).then(() => { window.location.href = "login.php"; });
});</script>';
exit();

323
admin/manage_developers.php Normal file
View File

@@ -0,0 +1,323 @@
<?php
require_once '../config.php';
// 设置会话cookie路径为根目录以确保跨目录访问
session_set_cookie_params(0, '/');
session_start();
// 检查是否已登录
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit();
}
// 检查权限
if ($_SESSION['admin']['permission'] != 'all') {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
// 处理退出登录
if (isset($_GET['logout'])) {
unset($_SESSION['admin']);
header('Location: login.php');
exit;
}
// 导航栏
?>
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container mt-4">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">App列表</a>
</li>
<li class="nav-item">
<a class="nav-link" href="addapp.php">添加App</a>
</li>
<li class="nav-item">
<a class="nav-link" href="review_apps.php">审核APP</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="manage_developers.php">管理开发者</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="confirmLogout()">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<?php
// 检查管理员权限
// 设置会话cookie路径为根目录以确保跨目录访问
session_set_cookie_params(0, '/');
// 检查会话是否已启动,避免重复启动
if (session_status() == PHP_SESSION_NONE) {
if (!session_start()) {
error_log('会话启动失败');
header('Location: login.php');
exit;
error_log('会话启动失败');
header('Location: login.php');
exit;
// 从数据库验证用户角色,确保权限检查准确性
if (isset($_SESSION['user_id'])) {
$userId = $_SESSION['user_id'];
$stmt = $conn->prepare("SELECT role FROM users WHERE id = ?");
if (!$stmt) {
error_log('Database prepare failed: ' . $conn->error);
header('Location: login.php');
exit;
}
$stmt->bind_param("i", $userId);
if (!$stmt->execute()) {
error_log('Query execution failed: ' . $stmt->error);
header('Location: login.php');
exit;
}
$result = $stmt->get_result();
if (!$result) {
error_log('Failed to get result: ' . $stmt->error);
header('Location: login.php');
exit;
}
$user = $result->fetch_assoc();
if (!$user || $user['role'] !== 'admin') {
error_log('用户 ' . $userId . ' 不是管理员,拒绝访问');
header('Location: login.php');
exit;
}
} else {
error_log('未找到用户会话,重定向到登录页');
header('Location: login.php');
exit;
}
}
}
// 处理删除用户请求
if (isset($_POST['delete_user'])) {
$userId = $_POST['user_id'];
$stmt = $conn->prepare("DELETE FROM users WHERE id = ? AND role = 'developer'");
if (!$stmt) {
error_log('Database prepare failed: ' . $conn->error);
header('Location: manage_developers.php?error=delete');
exit;
}
$stmt->bind_param("i", $userId);
if (!$stmt->execute()) {
error_log('Delete query execution failed: ' . $stmt->error);
header('Location: manage_developers.php?error=delete');
exit;
}
$affected_rows = $stmt->affected_rows;
$stmt->close();
if ($affected_rows > 0) {
header("Location: manage_developers.php?deleted=true");
} else {
error_log('No user deleted with ID: ' . $userId);
header('Location: manage_developers.php?error=delete&user_id=' . $userId);
}
exit;
}
// 处理更新用户请求
if (isset($_POST['update_user'])) {
$userId = $_POST['user_id'];
$username = $_POST['username'];
$email = $_POST['email'];
// 使用mysqli语法更新用户信息
$stmt = $conn->prepare("UPDATE developers SET username = ?, email = ? WHERE id = ?");
if (!$stmt) {
$error = $conn->error ?? 'Unknown error';
error_log("Prepare failed: $error");
die("更新用户信息失败: $error");
}
$stmt->bind_param("ssi", $username, $email, $userId);
if (!$stmt->execute()) {
$error = $stmt->error ?? 'Unknown error';
error_log("Execute failed: $error");
die("更新用户信息失败: $error");
}
$stmt->close();
header("Location: manage_developers.php?updated=true");
exit;
}
// 获取所有开发者用户
$developers = [];
// 检查developers表是否存在
$tableExists = $conn->query("SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'developers'");
if (!$tableExists || $tableExists->num_rows === 0) {
error_log('Developers table does not exist');
die('获取开发者列表失败: 开发者数据表不存在');
}
$sql = "SELECT * FROM developers ORDER BY id DESC";
$result = $conn->query($sql);
if (!$result) {
error_log('Failed to fetch developers. SQL: ' . $sql . ', Error: ' . $conn->error);
die('获取开发者列表失败: ' . $conn->error . ' (SQL: ' . $sql . ')');
}
// 检查是否有数据
$rowCount = $result->num_rows;
error_log('Developer query executed. Rows returned: ' . $rowCount);
while ($row = $result->fetch_assoc()) {
$developers[] = $row;
}
// 获取要编辑的用户信息
$editUser = null;
if (isset($_GET['edit'])) {
$editId = (int)$_GET['edit'];
$stmt = $conn->prepare("SELECT id, username, email FROM developers WHERE id = ?");
if (!$stmt) {
error_log('Prepare failed for edit user: ' . $conn->error);
die('获取编辑用户信息失败: ' . $conn->error);
}
$stmt->bind_param("i", $editId);
$stmt->execute();
$editUser = $stmt->get_result()->fetch_assoc();
$stmt->close();
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理开发者用户 - 应用商店管理</title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<!-- Fluent Design 模糊效果 -->
<style>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
</style>
<script src="/js/sweetalert.js"></script>
<script>
function confirmLogout() {
Swal.fire({
title: '确定要登出吗?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'logout.php';
}
});
}
</script>
</head>
<body>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
</script>
<div class="container">
<h1><i class="fas fa-users me-2"></i>管理开发者用户</h1>
<pre>调试信息:
查询SQL: <?php echo $sql; ?>
查询结果行数: <?php echo $rowCount; ?>
数据表存在: <?php echo $tableExists ? '是' : '否'; ?>
开发者数据: <?php print_r($developers); ?></pre>
<?php if (isset($_GET['deleted'])): ?>
<div class="alert alert-success">用户已成功删除</div>
<?php endif; ?>
<?php if (isset($_GET['updated'])): ?>
<div class="alert alert-success">用户信息已成功更新</div>
<?php endif; ?>
<?php if ($editUser): ?>
<div class="card mb-4">
<div class="card-header">
<h2><i class="fas fa-edit me-2"></i>编辑开发者用户</h2>
</div>
<div class="card-body">
<form method="post" action="manage_developers.php">
<input type="hidden" name="user_id" value="<?php echo $editUser['id']; ?>">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="username" name="username" value="<?php echo htmlspecialchars($editUser['username']); ?>" required>
<label for="username"><i class="fas fa-user me-2"></i>用户名</label>
</div>
<div class="form-floating mb-3">
<input type="email" class="form-control" id="email" name="email" value="<?php echo htmlspecialchars($editUser['email']); ?>" required>
<label for="email"><i class="fas fa-envelope me-2"></i>邮箱</label>
</div>
<button type="submit" name="update_user" class="btn btn-primary me-2"><i class="fas fa-save me-2"></i>更新用户</button>
<a href="manage_developers.php" class="btn btn-secondary"><i class="fas fa-times-circle me-2"></i>取消</a>
</form>
</div>
</div>
<?php endif; ?>
<table class="table table-striped">
<thead>
<tr>
<th><i class="fas fa-id-card me-2"></i>ID</th>
<th><i class="fas fa-user me-2"></i>用户名</th>
<th><i class="fas fa-envelope me-2"></i>邮箱</th>
<th><i class="fas fa-clock me-2"></i>注册时间</th>
<th><i class="fas fa-cog me-2"></i>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($developers as $developer): ?>
<tr>
<td><?php echo $developer['id']; ?></td>
<td><?php echo htmlspecialchars($developer['username']); ?></td>
<td><?php echo htmlspecialchars($developer['email']); ?></td>
<td><?php echo $developer['created_at']; ?></td>
<td>
<a href="manage_developers.php?edit=<?php echo $developer['id']; ?>" class="btn btn-sm btn-outline-primary"><i class="fas fa-edit me-1"></i>编辑</a>
<form method="post" action="manage_developers.php" style="display: inline-block;" onsubmit="return confirm('确定要删除这个用户吗?');">
<input type="hidden" name="user_id" value="<?php echo $developer['id']; ?>">
<button type="submit" name="delete_user" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash-alt me-1"></i>删除</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($developers)): ?>
<tr>
<td colspan="5" class="text-center">暂无开发者数据</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</body>
</html>

184
admin/manage_tags.php Normal file
View File

@@ -0,0 +1,184 @@
<?php
require_once '../config.php';
require_once 'login.php'; // 确保管理员已登录
// 检查权限
if ($_SESSION['admin']['permission'] != 'all') {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
// 处理标签添加
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_tag'])) {
$name = trim($_POST['tag_name']);
if (!empty($name)) {
$stmt = $conn->prepare("INSERT INTO tags (name) VALUES (?)");
$stmt->bind_param("s", $name);
if ($stmt->execute()) {
header('Location: manage_tags.php?success=标签添加成功');
exit;
} else {
$error = '添加失败: ' . $conn->error;
}
} else {
$error = '标签名称不能为空';
}
}
// 处理标签删除
if (isset($_GET['delete'])) {
$tagId = intval($_GET['delete']);
$stmt = $conn->prepare("DELETE FROM tags WHERE id = ?");
$stmt->bind_param("i", $tagId);
if ($stmt->execute()) {
header('Location: manage_tags.php?success=标签删除成功');
exit;
} else {
$error = '删除失败: ' . $conn->error;
}
}
// 处理标签编辑
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_tag'])) {
$tagId = intval($_POST['tag_id']);
$name = trim($_POST['tag_name']);
if (!empty($name)) {
$stmt = $conn->prepare("UPDATE tags SET name = ? WHERE id = ?");
$stmt->bind_param("si", $name, $tagId);
if ($stmt->execute()) {
header('Location: manage_tags.php?success=标签更新成功');
exit;
} else {
$error = '更新失败: ' . $conn->error;
}
} else {
$error = '标签名称不能为空';
}
}
// 获取所有标签
$tagsResult = $conn->query("SELECT * FROM tags ORDER BY created_at DESC");
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标签管理 - 应用商店后台</title>
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<link rel="stylesheet" href="../styles.css">
<script src="/js/sweetalert.js"></script>
<script>
function confirmLogout() {
Swal.fire({
title: '确定要登出吗?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'logout.php';
}
});
}
</script>
</head>
<body>
<div class="container mt-5">
<h1 class="mb-4">标签管理</h1>
<a href="index.php" class="btn btn-secondary mb-3"><i class="fas fa-arrow-left me-2"></i>返回应用列表</a>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success"><?php echo $_GET['success']; ?></div>
<?php endif; ?>
<?php if (isset($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<!-- 添加标签表单 -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-plus-circle me-2"></i>添加新标签</h5>
</div>
<div class="card-body">
<form method="post">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="tag_name" name="tag_name" required>
<label for="tag_name"><i class="fas fa-tag me-2"></i>标签名称</label>
</div>
<button type="submit" name="add_tag" class="btn btn-primary"><i class="fas fa-save me-2"></i>添加标签</button>
</form>
</div>
</div>
<!-- 标签列表 -->
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-list me-2"></i>现有标签</h5>
</div>
<div class="card-body">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>标签名称</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php while ($tag = $tagsResult->fetch_assoc()): ?>
<tr>
<td><?php echo $tag['id']; ?></td>
<td><?php echo htmlspecialchars($tag['name']); ?></td>
<td><?php echo $tag['created_at']; ?></td>
<td>
<!-- 编辑按钮触发模态框 -->
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editModal<?php echo $tag['id']; ?>">
<i class="fas fa-edit me-1"></i>编辑
</button>
<a href="manage_tags.php?delete=<?php echo $tag['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('确定要删除这个标签吗?关联的应用标签也会被删除。');"><i class="fas fa-trash-alt me-1"></i>删除</a>
</td>
</tr>
<!-- 编辑标签模态框 -->
<div class="modal fade" id="editModal<?php echo $tag['id']; ?>" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-edit me-2"></i>编辑标签</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="post">
<input type="hidden" name="tag_id" value="<?php echo $tag['id']; ?>">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="edit_tag_name<?php echo $tag['id']; ?>" name="tag_name" value="<?php echo htmlspecialchars($tag['name']); ?>" required>
<label for="edit_tag_name<?php echo $tag['id']; ?>"><i class="fas fa-tag me-2"></i>标签名称</label>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><i class="fas fa-times-circle me-2"></i>取消</button>
<button type="submit" name="edit_tag" class="btn btn-primary"><i class="fas fa-save me-2"></i>保存修改</button>
</div>
</form>
</div>
</div>
</div>
</div>
<?php endwhile; ?>
</tbody>
</table>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<li class="nav-item">
<a class="nav-link" href="#" onclick="confirmLogout()">退出登录</a>
</li>
</body>
</html>

383
admin/manage_versions.php Normal file
View File

@@ -0,0 +1,383 @@
<?php
require_once '../config.php';
session_start();
// 检查是否已登录
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit();
}
// 非全部权限管理员重定向到对应权限页面
if ($_SESSION['admin']['permission'] != 'all') {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
// 验证App ID
if (!isset($_GET['app_id']) || !is_numeric($_GET['app_id'])) {
header('Location: index.php?error=无效的App ID');
exit;
}
$appId = $_GET['app_id'];
// 获取App信息
$app = null;
$getAppSql = "SELECT * FROM apps WHERE id = ?";
$stmt = $conn->prepare($getAppSql);
$stmt->bind_param("i", $appId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
header('Location: index.php?error=App不存在');
exit;
}
$app = $result->fetch_assoc();
// 获取所有版本
$versions = [];
$getVersionsSql = "SELECT * FROM app_versions WHERE app_id = ? ORDER BY created_at DESC";
$stmt = $conn->prepare($getVersionsSql);
$stmt->bind_param("i", $appId);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
$versions[] = $row;
}
$success = '';
$error = '';
// 处理添加版本
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_version'])) {
$version = $_POST['version'];
$changelog = $_POST['changelog'];
if (empty($version)) {
$error = '版本号不能为空';
} elseif (empty($_FILES['app_file']['name'])) {
$error = '请上传App文件';
} else {
$uploadDir = '../files/';
$fileName = basename($_FILES['app_file']['name']);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
$insertVersionSql = "INSERT INTO app_versions (app_id, version, changelog, file_path, created_at) VALUES (?, ?, ?, ?, NOW())";
$stmt = $conn->prepare($insertVersionSql);
$stmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
if ($stmt->execute() === TRUE) {
header('Location: manage_versions.php?app_id=' . $appId . '&success=版本添加成功');
exit;
} else {
$error = '版本添加失败: ' . $conn->error;
unlink($targetPath); // 删除已上传的文件
}
} else {
$error = '文件上传失败';
}
}
}
// 处理删除版本
if (isset($_GET['delete_id']) && is_numeric($_GET['delete_id'])) {
$versionId = $_GET['delete_id'];
// 获取版本信息
$getVersionSql = "SELECT file_path FROM app_versions WHERE id = ? AND app_id = ?";
$stmt = $conn->prepare($getVersionSql);
$stmt->bind_param("ii", $versionId, $appId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 1) {
$version = $result->fetch_assoc();
// 删除文件
if (file_exists($version['file_path'])) {
unlink($version['file_path']);
}
// 删除数据库记录
$deleteVersionSql = "DELETE FROM app_versions WHERE id = ? AND app_id = ?";
$stmt = $conn->prepare($deleteVersionSql);
$stmt->bind_param("ii", $versionId, $appId);
if ($stmt->execute() === TRUE) {
header('Location: manage_versions.php?app_id=' . $appId . '&success=版本删除成功');
exit;
} else {
$error = '版本删除失败: ' . $conn->error;
}
} else {
$error = '版本不存在';
}
}
// 处理编辑版本
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_version'])) {
$versionId = $_POST['version_id'];
$version = $_POST['version'];
$changelog = $_POST['changelog'];
if (empty($version)) {
$error = '版本号不能为空';
} else {
// 检查是否上传了新文件
$fileUpdate = '';
$params = ['ss', $version, $changelog, $versionId, $appId];
if (!empty($_FILES['new_app_file']['name'])) {
$uploadDir = '../files/';
$fileName = basename($_FILES['new_app_file']['name']);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['new_app_file']['tmp_name'], $targetPath)) {
// 获取旧文件路径
$getOldFileSql = "SELECT file_path FROM app_versions WHERE id = ? AND app_id = ?";
$stmt = $conn->prepare($getOldFileSql);
$stmt->bind_param("ii", $versionId, $appId);
$stmt->execute();
$result = $stmt->get_result();
$oldVersion = $result->fetch_assoc();
// 删除旧文件
if (file_exists($oldVersion['file_path'])) {
unlink($oldVersion['file_path']);
}
$fileUpdate = ", file_path = ?";
$params[0] = 'sss';
$params[] = $targetPath;
} else {
$error = '文件上传失败';
}
}
if (empty($error)) {
$updateVersionSql = "UPDATE app_versions SET version = ?, changelog = ?" . $fileUpdate . " WHERE id = ? AND app_id = ?";
$stmt = $conn->prepare($updateVersionSql);
// 动态绑定参数
$stmt->bind_param(...$params);
if ($stmt->execute() === TRUE) {
header('Location: manage_versions.php?app_id=' . $appId . '&success=版本更新成功');
exit;
} else {
$error = '版本更新失败: ' . $conn->error;
}
}
}
}
// 获取URL参数中的成功/错误消息
if (isset($_GET['success'])) {
$success = $_GET['success'];
} elseif (isset($_GET['error'])) {
$error = $_GET['error'];
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理版本 - <?php echo htmlspecialchars($app['name']); ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<style>
.version-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.version-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.action-btn {
margin: 0 2px;
}
.modal-backdrop {
backdrop-filter: blur(5px);
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">App列表</a>
</li>
<li class="nav-item">
<a class="nav-link" href="editapp.php?id=<?php echo $appId; ?>">返回编辑App</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="manage_versions.php?app_id=<?php echo $appId; ?>">管理版本</a>
</li>
<li class="nav-item">
<a class="nav-link" href="?logout=true">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<div class="row mb-4">
<div class="col">
<h1><i class="fas fa-code-branch me-2"></i>管理版本: <?php echo htmlspecialchars($app['name']); ?></h1>
<p class="text-muted">管理该应用的所有版本</p>
</div>
<div class="col text-end">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addVersionModal">
<i class="fas fa-plus-circle me-2"></i>添加新版本
</button>
</div>
</div>
<?php if (!empty($success)): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<?php if (empty($versions)): ?>
<div class="alert alert-info">
暂无版本记录
</div>
<?php else: ?>
<div class="row">
<?php foreach ($versions as $version): ?>
<div class="col-md-6 col-lg-4 mb-4">
<div class="card version-card h-100">
<div class="card-body">
<h5 class="card-title"><i class="fas fa-tag me-2"></i>版本 <?php echo htmlspecialchars($version['version']); ?></h5>
<h6 class="card-subtitle mb-2 text-muted">发布日期: <?php echo date('Y-m-d H:i', strtotime($version['created_at'])); ?></h6>
<p class="card-text"><?php echo nl2br(htmlspecialchars($version['changelog'])); ?></p>
</div>
<div class="card-footer bg-transparent d-flex justify-content-between align-items-center">
<small class="text-muted">文件大小: <?php
$filePath = $version['file_path'];
if (file_exists($filePath)) {
echo filesize($filePath) > 0 ? number_format(filesize($filePath) / 1024 / 1024, 2) . ' MB' : '未知';
} else {
echo '文件不存在';
}
?></small>
<div> <button type="button" class="btn btn-sm btn-outline-secondary action-btn" data-bs-toggle="modal" data-bs-target="#editVersionModal_<?php echo $version['id']; ?>">
<i class="fas fa-edit me-1"></i>编辑
</button>
<a href="../<?php echo htmlspecialchars($version['file_path']); ?>" class="btn btn-sm btn-primary action-btn" download>
<i class="fas fa-download me-1"></i>下载
</a>
<a href="?app_id=<?php echo $appId; ?>&delete_id=<?php echo $version['id']; ?>" class="btn btn-sm btn-outline-danger action-btn" onclick="return confirm('确定要删除此版本吗?');">
<i class="fas fa-trash-alt me-1"></i>删除
</a>
</div>
</div>
</div>
<!-- 编辑版本模态框 -->
<div class="modal fade" id="editVersionModal_<?php echo $version['id']; ?>" tabindex="-1" aria-labelledby="editVersionModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editVersionModalLabel"><i class="fas fa-edit me-2"></i>编辑版本 <?php echo htmlspecialchars($version['version']); ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" enctype="multipart/form-data">
<div class="modal-body">
<input type="hidden" name="version_id" value="<?php echo $version['id']; ?>">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="version_<?php echo $version['id']; ?>" name="version" value="<?php echo htmlspecialchars($version['version']); ?>" placeholder="如: 1.0.0" required>
<label for="version_<?php echo $version['id']; ?>"><i class="fas fa-hashtag me-2"></i>版本号</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="changelog_<?php echo $version['id']; ?>" name="changelog" rows="3" placeholder="描述本次更新内容" required><?php echo htmlspecialchars($version['changelog']); ?></textarea>
<label for="changelog_<?php echo $version['id']; ?>"><i class="fas fa-history me-2"></i>更新日志</label>
</div>
<div class="mb-3">
<label for="new_app_file_<?php echo $version['id']; ?>" class="form-label"><i class="fas fa-upload me-2"></i>更新App文件 (可选)</label>
<input class="form-control" type="file" id="new_app_file_<?php echo $version['id']; ?>" name="new_app_file">
<div class="form-text">当前文件: <?php echo basename($version['file_path']); ?></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times-circle me-1"></i>取消
</button>
<button type="submit" class="btn btn-primary" name="edit_version">
<i class="fas fa-save me-1"></i>保存更改
</button>
</div>
</form>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- 添加版本模态框 -->
<div class="modal fade" id="addVersionModal" tabindex="-1" aria-labelledby="addVersionModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addVersionModalLabel"><i class="fas fa-plus-circle me-2"></i>添加新版本</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" enctype="multipart/form-data">
<div class="modal-body">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="version" name="version" placeholder="如: 1.0.0" required>
<label for="version"><i class="fas fa-hashtag me-2"></i>版本号</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="changelog" name="changelog" rows="3" placeholder="描述本次更新内容" required></textarea>
<label for="changelog"><i class="fas fa-history me-2"></i>更新日志</label>
</div>
<div class="mb-3">
<label for="app_file" class="form-label"><i class="fas fa-upload me-2"></i>App文件</label>
<input class="form-control" type="file" id="app_file" name="app_file" required>
<a href="<?php echo htmlspecialchars($version['file_path']); ?>" class="btn btn-sm btn-primary" download>下载</a>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary" name="add_version">添加版本</button>
</div>
</form>
</div>
</div>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
</script>
</body>
</html>

348
admin/review_apps.php Normal file
View File

@@ -0,0 +1,348 @@
<?php
require_once '../config.php';
require_once '../vendor/autoload.php';
require_once '../includes/logger.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
session_start();
// 检查是否已登录
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit();
}
// 检查权限 - 允许all和review权限
if (!in_array($_SESSION['admin']['permission'], ['all', 'review'])) {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'index.php';
header("Location: $redirect");
exit();
}
$success = '';
$error = '';
// 处理审核操作
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['review_action'])) {
$appId = $_POST['app_id'];
$action = $_POST['review_action'];
$rejectionReason = urldecode($_POST['rejection_reason'] ?? '');
// 验证应用ID
if (!is_numeric($appId)) {
$error = '无效的应用ID';
} else {
// 检查数据库连接
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
// 更新应用状态
$status = $action === 'approve' ? 'approved' : 'rejected';
$stmt = $conn->prepare("UPDATE apps SET status = ?, rejection_reason = ? WHERE id = ?");
if (!$stmt) {
$error = "数据库错误: " . $conn->error;
} else {
$stmt->bind_param("ssi", $status, $rejectionReason, $appId);
if ($stmt->execute()) {
// 获取应用信息和开发者邮箱
$getAppStmt = $conn->prepare("SELECT name, developer_email FROM apps WHERE id = ?");
$getAppStmt->bind_param("i", $appId);
$getAppStmt->execute();
$appResult = $getAppStmt->get_result();
$appInfo = $appResult->fetch_assoc();
$getAppStmt->close();
$success = '应用审核已更新';
$appName = $appInfo['name'] ?? '未知应用';
$devEmail = $appInfo['developer_email'] ?? '';
// 发送邮件通知
if (!empty($devEmail)) {
$mail = new PHPMailer(true);
try {
// 服务器配置
$mail->isSMTP();
$mail->Host = SMTP_HOST;
$mail->Port = SMTP_PORT;
$mail->SMTPSecure = SMTP_ENCRYPTION;
$mail->SMTPAuth = true;
$mail->Username = SMTP_USERNAME;
$mail->Password = SMTP_PASSWORD;
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
$mail->setFrom(SMTP_FROM_EMAIL, SMTP_FROM_NAME);
$mail->addAddress($devEmail);
// 邮件内容
if ($status === 'approved') {
$mail->Subject = '应用审核通过通知';
$mail->Body = "<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e0e0e0; border-radius: 8px;'>
<h2 style='color: #2c3e50;'>应用审核通过通知</h2>
<p>您好,</p>
<p>您的应用 <strong>{$appName}</strong> 已成功通过审核!</p>
<p>现在可以在应用商店中查看您的应用。</p>
<p style='margin-top: 20px; color: #666;'>此致<br>应用商店团队</p>
</div>";
} else {
$mail->Subject = '应用审核未通过通知';
$mail->Body = "<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e0e0e0; border-radius: 8px;'>
<h2 style='color: #e74c3c;'>应用审核未通过通知</h2>
<p>您好,</p>
<p>您的应用 <strong>{$appName}</strong> 未通过审核。</p>
<p>原因:<br>{$rejectionReason}</p>
<p style='margin-top: 20px; color: #666;'>此致<br>应用商店团队</p>
</div>";
}
$mail->send();
$success .= ',邮件通知已发送';
} catch (Exception $e) {
log_error("邮件发送失败: {$mail->ErrorInfo}", __FILE__, __LINE__);
$error = "审核状态已更新,但邮件发送失败: {$mail->ErrorInfo}";
}
}
} else {
$error = '更新审核状态失败: ' . $conn->error;
}
$stmt->close();
}
}
}
}
// 获取待审核应用列表
$pendingApps = [];
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
$stmt = $conn->prepare("SELECT a.id, a.name, a.description, a.status, a.created_at, d.username
FROM apps a
LEFT JOIN developers d ON a.developer_id = d.id
WHERE a.status = 'pending'
ORDER BY a.created_at DESC");
if (!$stmt) {
$error = "数据库错误: " . $conn->error;
} else {
$stmt->execute();
$result = $stmt->get_result();
$pendingApps = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>应用审核 - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- SweetAlert2 CSS -->
<link rel="stylesheet" href="/js/sweetalert.js/dist/sweetalert2.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
.app-card {
transition: transform 0.2s;
}
.app-card:hover {
transform: scale(1.02);
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">App列表</a>
</li>
<li class="nav-item">
<a class="nav-link" href="addapp.php">添加App</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="review_apps.php">应用审核</a>
</li>
<li class="nav-item">
<a class="nav-link" onclick="confirmLogout()">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<div class="alert alert-info">
请遵循应用商店的<a href="/docs/app_review_standards.php" target="_blank"><i class="fas fa-external-link-alt me-1"></i>审核规则</a>,确保应用符合平台要求。
</div>
<?php if (!empty($success)): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<h2><i class="fas fa-check-circle me-2"></i>应用审核</h2>
<p class="text-muted"><i class="fas fa-clock me-1"></i>待审核应用: <?php echo count($pendingApps); ?></p>
<?php if (empty($pendingApps)): ?>
<div class="alert alert-info">没有待审核的应用</div>
<?php else: ?>
<div class="row">
<?php foreach ($pendingApps as $app): ?>
<div class="col-md-6 mb-4">
<div class="card app-card shadow-sm">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0"><?php echo htmlspecialchars($app['name']); ?></h5>
</div>
<div class="card-body">
<p class="card-text"><?php echo htmlspecialchars($app['description']); ?></p>
<?php
$appId = $app['id'];
// 获取下载链接,假设在 app_versions 表中
$getDownloadLinkStmt = $conn->prepare("SELECT file_path FROM app_versions WHERE app_id = ? ORDER BY created_at DESC LIMIT 1");
if ($getDownloadLinkStmt) {
$getDownloadLinkStmt->bind_param("i", $appId);
$getDownloadLinkStmt->execute();
$downloadLinkResult = $getDownloadLinkStmt->get_result();
$downloadLinkInfo = $downloadLinkResult->fetch_assoc();
$downloadLink = $downloadLinkInfo ? $downloadLinkInfo['file_path'] : '';
$getDownloadLinkStmt->close();
} else {
$downloadLink = '';
log_error('数据库准备语句错误: ' . $conn->error, __FILE__, __LINE__);
}
// 获取应用标签
$getTagsStmt = $conn->prepare("SELECT t.name FROM tags t JOIN app_tags at ON t.id = at.tag_id WHERE at.app_id = ?");
if ($getTagsStmt) {
$getTagsStmt->bind_param("i", $appId);
$getTagsStmt->execute();
$tagsResult = $getTagsStmt->get_result();
$tags = [];
while ($tag = $tagsResult->fetch_assoc()) {
$tags[] = $tag['name'];
}
$tagString = implode(', ', $tags);
$getTagsStmt->close();
} else {
$tagString = '';
log_error('数据库准备语句错误: ' . $conn->error, __FILE__, __LINE__);
}
?>
<?php if (!empty($downloadLink)): ?>
<p class="card-text"><strong>下载链接:</strong> <a href="<?php echo htmlspecialchars('../' . $downloadLink); ?>" target="_blank">点击下载</a></p>
<?php endif; ?>
<?php if (!empty($tagString)): ?>
<p class="card-text"><strong>标签:</strong> <?php echo htmlspecialchars($tagString); ?></p>
<?php endif; ?>
<p class="card-text"><strong>开发者:</strong> <?php echo htmlspecialchars($app['username'] ?? '管理员'); ?></p>
<p class="card-text"><strong>提交时间:</strong> <?php echo htmlspecialchars($app['created_at']); ?></p>
<p class="card-text"><strong>描述:</strong> <?php echo nl2br(htmlspecialchars($app['description'])); ?></p>
<!-- 获取应用图片 -->
<?php
$images = [];
$stmt = $conn->prepare("SELECT image_path FROM app_images WHERE app_id = ?");
$stmt->bind_param("i", $app['id']);
$stmt->execute();
$imgResult = $stmt->get_result();
while ($img = $imgResult->fetch_assoc()) {
$images[] = $img['image_path'];
}
$stmt->close();
?>
<?php if (!empty($images)): ?>
<div class="mb-3">
<strong>预览图片:</strong><br>
<img src="<?php echo htmlspecialchars($images[0]); ?>" alt="应用截图" class="img-thumbnail" style="max-width: 200px;">
</div>
<?php endif; ?>
<form method="post" class="mt-3">
<input type="hidden" name="app_id" value="<?php echo $app['id']; ?>">
<div class="d-flex gap-2">
<button type="submit" name="review_action" value="approve" class="btn btn-success me-2"><i class="fas fa-check me-1"></i>通过</button>
<button type="button" class="btn btn-danger" onclick="showRejectReason(<?php echo $app['id']; ?>, '<?php echo addslashes(htmlspecialchars($app['name'])); ?>')"><i class="fas fa-times me-1"></i>拒绝</button>
</div>
</form>
</div>
</div>
</div>
<script>
function showRejectReason(appId, appName) {
Swal.fire({
title: '拒绝应用: ' + appName,
html: '<textarea id="rejectionReason" class="swal2-textarea" rows="3" placeholder="请详细说明拒绝原因,帮助开发者改进应用"></textarea>',
confirmButtonText: '确认拒绝',
cancelButtonText: '取消',
showCancelButton: true,
validationMessage: '请输入拒绝原因',
preConfirm: () => {
const reason = document.getElementById('rejectionReason').value;
if (!reason) {
Swal.showValidationMessage('请输入拒绝原因');
}
return reason;
}
}).then((result) => {
if (result.isConfirmed) {
const form = document.createElement('form');
form.method = 'post';
form.innerHTML = `
<input type="hidden" name="app_id" value="${appId}">
<input type="hidden" name="review_action" value="reject">
<input type="hidden" name="rejection_reason" value="${encodeURIComponent(result.value)}">
`;
document.body.appendChild(form);
form.submit();
}
});
}
</script>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- Bootstrap JS with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<!-- SweetAlert2 JS -->
<script src="/js/sweetalert.js"></script>
<script>
function confirmLogout() {
Swal.fire({
title: '确认退出登录?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '确认',
cancelButtonText: '取消',
reverseButtons: true
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'logout.php';
}
});
}
</script>
</body>
</html>

205
admin/system_info.php Normal file
View File

@@ -0,0 +1,205 @@
<?php
session_start();
require_once '../config.php';
// 删除文件
function delete_file($file_path) {
if (file_exists($file_path)) {
return unlink($file_path);
}
return false;
}
// 处理删除请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$upload_dirs = [
'../uploads/apps',
'../uploads/images'
];
// 全量删除
if (isset($_POST['delete_all'])) {
foreach ($upload_dirs as $dir) {
if (is_dir($dir)) {
$files = scandir($dir);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
$file_path = $dir . '/' . $file;
if (is_file($file_path)) {
delete_file($file_path);
}
}
}
}
}
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
// 单个删除
if (isset($_POST['delete_files'])) {
foreach ($_POST['delete_files'] as $file_info) {
list($type, $filename) = explode('|', $file_info);
$dir = $type === '图片' ? '../uploads/images' : '../uploads/apps';
$file_path = $dir . '/' . $filename;
delete_file($file_path);
}
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
}
// 验证管理员权限
if (!isset($_SESSION['admin'])) {
header('Location: login.php');
exit();
}
// 非全部权限管理员重定向到对应权限页面
if ($_SESSION['admin']['permission'] != 'all') {
$redirect = $_SESSION['admin']['permission'] == 'say' ? 'announcements.php' : 'review_apps.php';
header("Location: $redirect");
exit();
}
// 获取上传文件和图片信息
function get_uploaded_files_info() {
$uploaded_files = [];
// 上传目录配置
$upload_dirs = [
'../uploads/apps',
'../uploads/images'
];
foreach ($upload_dirs as $dir) {
if (is_dir($dir)) {
$files = scandir($dir);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
$file_path = $dir . '/' . $file;
if (is_file($file_path)) {
$file_size = filesize($file_path);
$uploaded_files[] = [
'name' => $file,
'size' => $file_size,
'type' => strpos($dir, 'images') !== false ? '图片' : '文件'
];
}
}
}
}
}
return $uploaded_files;
}
$uploaded_files = get_uploaded_files_info();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>系统信息 - 上传文件列表</title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<link rel="stylesheet" href="../styles.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="index.php">管理员面板</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">首页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="system_info.php">系统信息</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<form method="post">
<h2><i class="fas fa-file-alt me-2"></i>上传文件信息</h2>
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" id="selectAll"></th>
<th><i class="fas fa-file-name me-2"></i>文件名</th>
<th><i class="fas fa-database me-2"></i>大小</th>
</tr>
</thead>
<tbody>
<?php foreach ($uploaded_files as $file): ?>
<?php if ($file['type'] === '文件'): ?>
<tr>
<td><input type="checkbox" name="delete_files[]" value="<?php echo $file['type'] . '|' . $file['name']; ?>"></td>
<td><?php echo htmlspecialchars($file['name']); ?></td>
<td><?php echo round($file['size'] / 1024, 2); ?> KB</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
<h2><i class="fas fa-image me-2"></i>上传图片信息</h2>
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" id="selectAllImages"></th>
<th><i class="fas fa-file-image me-2"></i>文件名</th>
<th><i class="fas fa-database me-2"></i>大小</th>
</tr>
</thead>
<tbody>
<?php foreach ($uploaded_files as $file): ?>
<?php if ($file['type'] === '图片'): ?>
<tr>
<td><input type="checkbox" name="delete_files[]" value="<?php echo $file['type'] . '|' . $file['name']; ?>"></td>
<td><?php echo htmlspecialchars($file['name']); ?></td>
<td><?php echo round($file['size'] / 1024, 2); ?> KB</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
<button type="submit" name="delete_all" class="btn btn-danger" onclick="return confirm('确定要删除所有文件吗?')"><i class="fas fa-trash-alt me-2"></i>全量删除</button>
<button type="submit" class="btn btn-danger ms-2" onclick="return confirm('确定要删除选中的文件吗?')"><i class="fas fa-trash-alt me-2"></i>删除选中</button>
</form>
</div>
</form>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
document.getElementById('selectAll').addEventListener('change', function() {
const checkboxes = document.querySelectorAll('input[name="delete_files[]"]');
checkboxes.forEach(checkbox => {
checkbox.checked = this.checked;
});
});
document.getElementById('selectAllImages').addEventListener('change', function() {
const checkboxes = document.querySelectorAll('input[name="delete_files[]"]');
checkboxes.forEach(checkbox => {
checkbox.checked = this.checked;
});
});
</script>
</body>
</html>

62
admin/test_config.php Normal file
View File

@@ -0,0 +1,62 @@
<?php
require_once '../config.php';
session_start();
// 输出配置和会话信息
echo '<!DOCTYPE html><html><head><title>配置测试</title></head><body>';
echo '<h1>配置文件测试</h1>';
echo '<h2>常量配置</h2>';
echo 'APP_STORE_NAME: ' . (defined('APP_STORE_NAME') ? APP_STORE_NAME : '未定义') . '<br>';
echo 'DB_HOST: ' . (defined('DB_HOST') ? DB_HOST : '未定义') . '<br>';
echo 'DB_USER: ' . (defined('DB_USER') ? DB_USER : '未定义') . '<br>';
echo 'DB_NAME: ' . (defined('DB_NAME') ? DB_NAME : '未定义') . '<br>';
echo 'DEBUG_MODE: ' . (defined('DEBUG_MODE') ? (DEBUG_MODE ? 'true' : 'false') : '未定义') . '<br>';
echo '<h2>管理员账户</h2>';
if (isset($admin_accounts) && is_array($admin_accounts)) {
echo '找到 ' . count($admin_accounts) . ' 个管理员账户<br>';
foreach ($admin_accounts as $account) {
echo '用户名: ' . $account['username'] . ', 权限: ' . $account['permission'] . '<br>';
}
} else {
echo '未找到管理员账户配置<br>';
}
echo '<h2>数据库连接</h2>';
if (isset($conn) && $conn instanceof mysqli) {
echo '数据库连接对象存在<br>';
if ($conn->ping()) {
echo '数据库连接成功<br>';
} else {
echo '数据库连接失败: ' . $conn->error . '<br>';
}
} else {
echo '数据库连接对象不存在<br>';
}
echo '<h2>会话信息</h2>';
echo '会话ID: ' . session_id() . '<br>';
echo '管理员登录状态: ' . (isset($_SESSION['admin']) ? '已登录' : '未登录') . '<br>';
if (isset($_SESSION['admin'])) {
echo '管理员ID: ' . $_SESSION['admin']['id'] . '<br>';
echo '管理员用户名: ' . $_SESSION['admin']['username'] . '<br>';
echo '管理员权限: ' . $_SESSION['admin']['permission'] . '<br>';
}
echo '<h2>权限检查测试</h2>';
echo 'index.php访问检查: ';
if (isset($_SESSION['admin']) && $_SESSION['admin']['permission'] == 'all') {
echo '允许访问<br>';
} else {
echo '将被重定向<br>';
}
echo 'review_apps.php访问检查: ';
if (isset($_SESSION['admin']) && in_array($_SESSION['admin']['permission'], ['all', 'review'])) {
echo '允许访问<br>';
} else {
echo '将被重定向<br>';
}
echo '</body></html>';

573
api.php Normal file
View File

@@ -0,0 +1,573 @@
<?php
/**
* App Store API
* 版本: 2.0
* 支持的端点:
* - /api.php?t=getallapps - 获取所有APP列表
* - /api.php?t=getappinfo&id={appid} - 获取APP信息
* - /api.php?t=getalltags - 获取所有标签
* - /api.php?t=gettagapp&id={tagid} - 获取某标签里的APP列表
* - /api.php?t=getdeveloperapp&id={developerid} - 获取某开发者的APP列表
* - /api.php?t=getdeveloperinfo&id={developerid} - 获取开发者信息
* - /api.php?t=getacc - 获取所有公告
* - /api.php?t=getcount - 获取计数信息
*/
// 关闭错误显示(生产环境)
ini_set('display_errors', 0);
error_reporting(E_ALL);
// 包含配置文件
require_once 'config.php';
// 设置JSON响应头
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
// 处理OPTIONS请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
// 检查数据库连接
if (!$conn) {
sendErrorResponse('数据库连接失败', 500);
exit;
}
// 获取API类型参数
$apiType = isset($_GET['t']) ? trim($_GET['t']) : '';
// 根据API类型调用对应处理函数
switch ($apiType) {
case 'getallapps':
getAllApps();
break;
case 'getappinfo':
getAppInfo();
break;
case 'getalltags':
getAllTags();
break;
case 'gettagapp':
getTagApps();
break;
case 'getdeveloperapp':
getDeveloperApps();
break;
case 'getdeveloperinfo':
getDeveloperInfo();
break;
case 'getacc':
getAllAnnouncements();
break;
case 'getcount':
getCountInfo();
break;
default:
showApiEndpoints();
break;
}
/**
* 发送成功响应
* @param mixed $data 要返回的数据
*/
function sendSuccessResponse($data) {
$response = [
'status' => 'success',
'data' => $data
];
echo json_encode($response, JSON_UNESCAPED_UNICODE);
exit;
}
/**
* 发送错误响应
* @param string $message 错误消息
* @param int $statusCode HTTP状态码
*/
function sendErrorResponse($message, $statusCode = 400) {
http_response_code($statusCode);
$response = [
'status' => 'error',
'message' => $message
];
echo json_encode($response, JSON_UNESCAPED_UNICODE);
exit;
}
/**
* 显示所有API端点信息
*/
function showApiEndpoints() {
$endpoints = [
'/api.php?t=getallapps' => '获取所有APP列表',
'/api.php?t=getappinfo&id={appid}' => '获取APP信息',
'/api.php?t=getalltags' => '获取所有标签',
'/api.php?t=gettagapp&id={tagid}' => '获取某标签里的APP列表',
'/api.php?t=getdeveloperapp&id={developerid}' => '获取某开发者的APP列表',
'/api.php?t=getdeveloperinfo&id={developerid}' => '获取开发者信息',
'/api.php?t=getacc' => '获取所有公告',
'/api.php?t=getcount' => '获取计数信息如所有APP的数量、开发者的数量等'
];
$response = [
'status' => 'success',
'message' => 'App Store API v2.0',
'endpoints' => $endpoints,
'example' => '/api.php?t=getallapps'
];
echo json_encode($response, JSON_UNESCAPED_UNICODE);
exit;
}
/**
* 获取所有APP列表
*/
function getAllApps() {
global $conn;
// 获取分页参数
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20;
$offset = ($page - 1) * $limit;
// 获取总数量
$countSql = "SELECT COUNT(*) as total FROM apps WHERE status = 'approved' AND is_approved = 1";
$countResult = $conn->query($countSql);
$total = $countResult->fetch_assoc()['total'] ?? 0;
$totalPages = ceil($total / $limit);
// 获取应用列表
$sql = "SELECT apps.id, apps.name, apps.description, apps.age_rating, apps.version,
AVG(reviews.rating) as avg_rating, SUM(app_versions.download_count) as total_downloads
FROM apps
LEFT JOIN reviews ON apps.id = reviews.app_id
LEFT JOIN app_versions ON apps.id = app_versions.app_id
WHERE apps.status = 'approved' AND apps.is_approved = 1
GROUP BY apps.id, apps.name, apps.description, apps.age_rating, apps.version
ORDER BY apps.created_at DESC
LIMIT ?, ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ii", $offset, $limit);
$stmt->execute();
$result = $stmt->get_result();
$apps = [];
while ($row = $result->fetch_assoc()) {
// 格式化JSON字段
if (isset($row['platforms']) && !empty($row['platforms'])) {
$row['platforms'] = json_decode($row['platforms'], true);
}
$apps[] = $row;
}
$response = [
'apps' => $apps,
'pagination' => [
'total' => $total,
'page' => $page,
'limit' => $limit,
'totalPages' => $totalPages
]
];
sendSuccessResponse($response);
}
/**
* 获取指定APP的详细信息
*/
function getAppInfo() {
global $conn;
// 检查是否提供了app_id参数
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
sendErrorResponse('请提供有效的应用ID', 400);
}
$appId = intval($_GET['id']);
// 获取应用基本信息
$sqlApp = "SELECT apps.*, AVG(reviews.rating) as avg_rating, SUM(app_versions.download_count) as total_downloads
FROM apps
LEFT JOIN reviews ON apps.id = reviews.app_id
LEFT JOIN app_versions ON apps.id = app_versions.app_id
WHERE apps.id = ? AND apps.status = 'approved' AND apps.is_approved = 1
GROUP BY apps.id";
$stmtApp = $conn->prepare($sqlApp);
$stmtApp->bind_param("i", $appId);
$stmtApp->execute();
$resultApp = $stmtApp->get_result();
if ($resultApp->num_rows === 0) {
sendErrorResponse('未找到该应用', 404);
}
$app = $resultApp->fetch_assoc();
// 格式化JSON字段
if (isset($app['platforms']) && !empty($app['platforms'])) {
$app['platforms'] = json_decode($app['platforms'], true);
}
// 获取版本信息
$sqlVersions = "SELECT id, version, changelog, file_path, download_count, created_at
FROM app_versions
WHERE app_id = ?
ORDER BY created_at DESC";
$stmtVersions = $conn->prepare($sqlVersions);
$stmtVersions->bind_param("i", $appId);
$stmtVersions->execute();
$resultVersions = $stmtVersions->get_result();
$versions = [];
while ($version = $resultVersions->fetch_assoc()) {
$versions[] = $version;
}
$app['versions'] = $versions;
// 获取图片信息
$sqlImages = "SELECT id, image_path FROM app_images WHERE app_id = ?";
$stmtImages = $conn->prepare($sqlImages);
$stmtImages->bind_param("i", $appId);
$stmtImages->execute();
$resultImages = $stmtImages->get_result();
$images = [];
while ($image = $resultImages->fetch_assoc()) {
$images[] = $image;
}
$app['images'] = $images;
// 获取标签信息
$sqlTags = "SELECT tags.id, tags.name FROM app_tags
JOIN tags ON app_tags.tag_id = tags.id
WHERE app_tags.app_id = ?";
$stmtTags = $conn->prepare($sqlTags);
$stmtTags->bind_param("i", $appId);
$stmtTags->execute();
$resultTags = $stmtTags->get_result();
$tags = [];
while ($tag = $resultTags->fetch_assoc()) {
$tags[] = $tag;
}
$app['tags'] = $tags;
sendSuccessResponse($app);
}
/**
* 获取所有标签
*/
function getAllTags() {
global $conn;
$sql = "SELECT id, name FROM tags ORDER BY name ASC";
$result = $conn->query($sql);
$tags = [];
while ($row = $result->fetch_assoc()) {
$tags[] = $row;
}
sendSuccessResponse($tags);
}
/**
* 获取指定标签下的应用列表
*/
function getTagApps() {
global $conn;
// 检查是否提供了tag_id参数
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
sendErrorResponse('请提供有效的标签ID', 400);
}
$tagId = intval($_GET['id']);
// 获取分页参数
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20;
$offset = ($page - 1) * $limit;
// 获取标签信息
$sqlTag = "SELECT name FROM tags WHERE id = ?";
$stmtTag = $conn->prepare($sqlTag);
$stmtTag->bind_param("i", $tagId);
$stmtTag->execute();
$resultTag = $stmtTag->get_result();
if ($resultTag->num_rows === 0) {
sendErrorResponse('未找到该标签', 404);
}
$tagInfo = $resultTag->fetch_assoc();
// 获取总数量
$countSql = "SELECT COUNT(DISTINCT apps.id) as total
FROM apps
JOIN app_tags ON apps.id = app_tags.app_id
WHERE app_tags.tag_id = ? AND apps.status = 'approved' AND apps.is_approved = 1";
$countStmt = $conn->prepare($countSql);
$countStmt->bind_param("i", $tagId);
$countStmt->execute();
$countResult = $countStmt->get_result();
$total = $countResult->fetch_assoc()['total'] ?? 0;
$totalPages = ceil($total / $limit);
// 获取应用列表
$sql = "SELECT apps.id, apps.name, apps.description, apps.age_rating, apps.version,
AVG(reviews.rating) as avg_rating
FROM apps
JOIN app_tags ON apps.id = app_tags.app_id
LEFT JOIN reviews ON apps.id = reviews.app_id
WHERE app_tags.tag_id = ? AND apps.status = 'approved' AND apps.is_approved = 1
GROUP BY apps.id, apps.name, apps.description, apps.age_rating, apps.version
ORDER BY apps.created_at DESC
LIMIT ?, ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("iii", $tagId, $offset, $limit);
$stmt->execute();
$result = $stmt->get_result();
$apps = [];
while ($row = $result->fetch_assoc()) {
$apps[] = $row;
}
$response = [
'tag' => $tagInfo,
'apps' => $apps,
'pagination' => [
'total' => $total,
'page' => $page,
'limit' => $limit,
'totalPages' => $totalPages
]
];
sendSuccessResponse($response);
}
/**
* 获取指定开发者的应用列表
*/
function getDeveloperApps() {
global $conn;
// 检查是否提供了developer_id参数
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
sendErrorResponse('请提供有效的开发者ID', 400);
}
$developerId = intval($_GET['id']);
// 获取分页参数
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20;
$offset = ($page - 1) * $limit;
// 获取开发者邮箱用于关联apps表
$sqlDeveloper = "SELECT email FROM developers WHERE id = ?";
$stmtDeveloper = $conn->prepare($sqlDeveloper);
$stmtDeveloper->bind_param("i", $developerId);
$stmtDeveloper->execute();
$resultDeveloper = $stmtDeveloper->get_result();
if ($resultDeveloper->num_rows === 0) {
sendErrorResponse('未找到该开发者', 404);
}
$developerInfo = $resultDeveloper->fetch_assoc();
$developerEmail = $developerInfo['email'];
// 获取总数量
$countSql = "SELECT COUNT(*) as total FROM apps WHERE developer_email = ? AND status = 'approved' AND is_approved = 1";
$countStmt = $conn->prepare($countSql);
$countStmt->bind_param("s", $developerEmail);
$countStmt->execute();
$countResult = $countStmt->get_result();
$total = $countResult->fetch_assoc()['total'] ?? 0;
$totalPages = ceil($total / $limit);
// 获取应用列表
$sql = "SELECT apps.id, apps.name, apps.description, apps.age_rating, apps.version,
AVG(reviews.rating) as avg_rating, SUM(app_versions.download_count) as total_downloads
FROM apps
LEFT JOIN reviews ON apps.id = reviews.app_id
LEFT JOIN app_versions ON apps.id = app_versions.app_id
WHERE apps.developer_email = ? AND apps.status = 'approved' AND apps.is_approved = 1
GROUP BY apps.id, apps.name, apps.description, apps.age_rating, apps.version
ORDER BY apps.created_at DESC
LIMIT ?, ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("sii", $developerEmail, $offset, $limit);
$stmt->execute();
$result = $stmt->get_result();
$apps = [];
while ($row = $result->fetch_assoc()) {
$apps[] = $row;
}
$response = [
'developer_id' => $developerId,
'apps' => $apps,
'pagination' => [
'total' => $total,
'page' => $page,
'limit' => $limit,
'totalPages' => $totalPages
]
];
sendSuccessResponse($response);
}
/**
* 获取开发者信息
*/
function getDeveloperInfo() {
global $conn;
// 检查是否提供了developer_id参数
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
sendErrorResponse('请提供有效的开发者ID', 400);
}
$developerId = intval($_GET['id']);
// 获取开发者信息(不包含密码等敏感信息)
$sql = "SELECT id, username, email, created_at, is_verified, verified_at
FROM developers
WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $developerId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
sendErrorResponse('未找到该开发者', 404);
}
$developer = $result->fetch_assoc();
// 获取开发者的应用数量
$appCountSql = "SELECT COUNT(*) as app_count
FROM apps
WHERE developer_email = ? AND status = 'approved' AND is_approved = 1";
$appCountStmt = $conn->prepare($appCountSql);
$appCountStmt->bind_param("s", $developer['email']);
$appCountStmt->execute();
$appCountResult = $appCountStmt->get_result();
$appCount = $appCountResult->fetch_assoc()['app_count'] ?? 0;
$developer['app_count'] = $appCount;
sendSuccessResponse($developer);
}
/**
* 获取所有公告
*/
function getAllAnnouncements() {
global $conn;
// 获取分页参数
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$limit = isset($_GET['limit']) ? min(100, max(1, intval($_GET['limit']))) : 20;
$offset = ($page - 1) * $limit;
// 获取总数量
$countSql = "SELECT COUNT(*) as total FROM announcements";
$countResult = $conn->query($countSql);
$total = $countResult->fetch_assoc()['total'] ?? 0;
$totalPages = ceil($total / $limit);
// 获取公告列表
$sql = "SELECT id, title, content, created_at, admin_id
FROM announcements
ORDER BY created_at DESC
LIMIT ?, ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ii", $offset, $limit);
$stmt->execute();
$result = $stmt->get_result();
$announcements = [];
while ($row = $result->fetch_assoc()) {
$announcements[] = $row;
}
$response = [
'announcements' => $announcements,
'pagination' => [
'total' => $total,
'page' => $page,
'limit' => $limit,
'totalPages' => $totalPages
]
];
sendSuccessResponse($response);
}
/**
* 获取计数信息
*/
function getCountInfo() {
global $conn;
// 应用总数
$appCountSql = "SELECT COUNT(*) as count FROM apps WHERE status = 'approved' AND is_approved = 1";
$appCountResult = $conn->query($appCountSql);
$appCount = $appCountResult->fetch_assoc()['count'] ?? 0;
// 开发者总数
$developerCountSql = "SELECT COUNT(*) as count FROM developers";
$developerCountResult = $conn->query($developerCountSql);
$developerCount = $developerCountResult->fetch_assoc()['count'] ?? 0;
// 标签总数
$tagCountSql = "SELECT COUNT(*) as count FROM tags";
$tagCountResult = $conn->query($tagCountSql);
$tagCount = $tagCountResult->fetch_assoc()['count'] ?? 0;
// 公告总数
$announcementCountSql = "SELECT COUNT(*) as count FROM announcements";
$announcementCountResult = $conn->query($announcementCountSql);
$announcementCount = $announcementCountResult->fetch_assoc()['count'] ?? 0;
// 总下载量
$downloadCountSql = "SELECT SUM(download_count) as count FROM app_versions";
$downloadCountResult = $conn->query($downloadCountSql);
$downloadCount = $downloadCountResult->fetch_assoc()['count'] ?? 0;
$counts = [
'total_apps' => $appCount,
'total_developers' => $developerCount,
'total_tags' => $tagCount,
'total_announcements' => $announcementCount,
'total_downloads' => $downloadCount
];
sendSuccessResponse($counts);
}
// 关闭数据库连接
$conn->close();
?>

515
app.php Normal file
View File

@@ -0,0 +1,515 @@
<?php
session_start();
require_once 'config.php';
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header('Location: index.php');
exit;
}
$appId = $_GET['id'];
// 获取App信息
$sqlApp = "SELECT apps.*, apps.developer_id, developers.username as developer_name, AVG(reviews.rating) as avg_rating
FROM apps
LEFT JOIN developers ON apps.developer_id = developers.id
LEFT JOIN reviews ON apps.id = reviews.app_id
WHERE apps.id = $appId
GROUP BY apps.id, apps.developer_id, developers.username";
$resultApp = $conn->query($sqlApp);
if (!$resultApp) {
die("<h1>数据库查询错误</h1><p>错误信息: " . htmlspecialchars($conn->error) . "</p><p>SQL语句: " . htmlspecialchars($sqlApp) . "</p>");
}
$app = $resultApp->fetch_assoc();
$developerId = $app['developer_id'] ?? 0;
$developerName = ($developerId == 0) ? '管理员' : ($app['developer_name'] ?? '未知开发者');
if (!$app) {
die("<h1>错误:应用不存在</h1><p>找不到ID为 $appId 的应用。请检查ID是否正确。</p>");
}
// 检查应用审核状态
if ($app['status'] != 'approved') {
echo '<script>
document.addEventListener("DOMContentLoaded", function() {
Swal.fire({
title: "应用审核中",
text: "该应用正在审核中,暂时无法访问。",
icon: "info",
confirmButtonText: "确定"
}).then((result) => {
if (result.isConfirmed) {
window.history.back();
}
});
});
</script>';
}
// 处理评价加载请求
if (isset($_GET['action']) && $_GET['action'] === 'load_reviews') {
header('Content-Type: text/html; charset=UTF-8');
// 获取评论数据
$sqlReviews = "SELECT * FROM reviews WHERE app_id = ? ORDER BY created_at DESC, id DESC LIMIT 10 OFFSET ?";
$stmt = $conn->prepare($sqlReviews);
$offset = isset($_GET['offset']) ? intval($_GET['offset']) : 0;
$stmt->bind_param("ii", $appId, $offset);
$stmt->execute();
$resultReviews = $stmt->get_result();
if (!$resultReviews) {
die("Error fetching reviews: " . htmlspecialchars($conn->error));
}
while ($review = $resultReviews->fetch_assoc()) {
?>
<div class="card mb-3 blur-bg">
<div class="card-body">
<?php
$rating = $review['rating'] !== null ? $review['rating'] : 0;
echo '<p class="card-text">评分: ';
for ($i = 1; $i <= 5; $i++) {
if ($i <= floor($rating)) {
echo '<span class="fas fa-star text-warning"></span>';
} elseif ($i - $rating <= 0.5) {
echo '<span class="fas fa-star-half-alt text-warning"></span>';
} else {
echo '<span class="far fa-star text-warning"></span>';
}
}
echo '</p>';
?>
<p class="card-text"><small class="text-muted">评价时间: <?php echo $review['created_at']; ?></small></p>
</div>
</div>
<?php
}
exit;
}
// 获取App版本信息
$sqlVersions = "SELECT * FROM app_versions WHERE app_id = $appId ORDER BY created_at DESC";
$resultVersions = $conn->query($sqlVersions);
// 获取App预览图片
$sqlImages = "SELECT * FROM app_images WHERE app_id = $appId";
$resultImages = $conn->query($sqlImages);
// 获取评价总数
$sqlReviewCount = "SELECT COUNT(*) as total FROM reviews WHERE app_id = $appId";
$resultReviewCount = $conn->query($sqlReviewCount);
$reviewCount = $resultReviewCount->fetch_assoc()['total'];
// 分页参数
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$hasMore = ($page * $limit) < $reviewCount;
// 获取评价信息
$sqlReviews = "SELECT * FROM reviews WHERE app_id = $appId ORDER BY created_at DESC, id DESC LIMIT 10 OFFSET $offset";
$resultReviews = $conn->query($sqlReviews);
// 获取评分分布
$sqlRatingDistribution = "SELECT rating, COUNT(*) as count FROM reviews WHERE app_id = $appId GROUP BY rating ORDER BY rating DESC";
$resultRatingDistribution = $conn->query($sqlRatingDistribution);
$ratingDistribution = [];
while ($row = $resultRatingDistribution->fetch_assoc()) {
$ratingDistribution[$row['rating']] = $row['count'];
}
// 处理评价提交
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['rating'])) {
$rating = $_POST['rating'];
$ipAddress = $_SERVER['REMOTE_ADDR'];
$insertSql = "INSERT INTO reviews (app_id, rating) VALUES ($appId, $rating)"; if ($conn->query($insertSql) === TRUE) { header("Location: app.php?id=$appId"); exit; }
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $app['name']; ?> - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- SweetAlert2 -->
<link rel="stylesheet" href="/js/sweetalert.js/dist/sweetalert2.min.css">
<script src="/js/sweetalert.js/dist/sweetalert2.all.min.js"></script>
<!-- 本地 Chart.js -->
<script src="js/charts.js"></script>
<!-- Marked.js 用于Markdown解析 -->
<script src="js/marked.js"></script>
<!-- 自定义CSS -->
<link rel="stylesheet" href="styles.css">
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">首页</a>
</li>
<?php if (isset($_SESSION['admin'])): ?>
<li class="nav-item">
<a class="nav-link" href="/admin/">管理</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<div class="row">
<div class="col-md-6">
<h1><i class="fas fa-box-open"></i> <?php echo $app['name']; ?></h1>
<div class="lead"><i class="fas fa-info-circle"></i> <div id="app-description"><?php echo htmlspecialchars($app['description']); ?></div></div>
<p><i class="fas fa-shield-alt"></i> 年龄分级:
<?php
$svgHeader = '<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"><defs><filter id="shadow"><feGaussianBlur in="SourceAlpha" stdDeviation="2"/><feOffset dx="0" dy="2" result="offsetblur"/><feFlood flood-color="rgba(0,0,0,0.2)"/><feComposite in2="offsetblur" operator="in"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs>';
$esrbIcons = [
'3+' => "{$svgHeader}<circle cx='20' cy='20' r='18' fill='#34A853' filter='url(#shadow)'/><text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' fill='white' font-size='14' font-weight='bold'>3+</text></svg>",
'7+' => "{$svgHeader}<circle cx='20' cy='20' r='18' fill='#FBBC05' filter='url(#shadow)'/><text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' fill='white' font-size='14' font-weight='bold'>7+</text></svg>",
'12+' => "{$svgHeader}<circle cx='20' cy='20' r='18' fill='#FF8C00' filter='url(#shadow)'/><text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' fill='white' font-size='14' font-weight='bold'>12+</text></svg>",
// '16+' => "{$svgHeader}<circle cx='20' cy='20' r='18' fill='#FBBC05' filter='url(#shadow)'/><text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' fill='white' font-size='14' font-weight='bold'>16+</text></svg>",
'17+' => "{$svgHeader}<circle cx='20' cy='20' r='18' fill='#EA4335' filter='url(#shadow)'/><text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle' fill='white' font-size='14' font-weight='bold'>17+</text></svg>"
];
echo isset($esrbIcons[$app['age_rating']]) ? $esrbIcons[$app['age_rating']] : '<i class="fas fa-user-check"></i>';
?></p>
<?php if (!empty($app['age_rating_description'])): ?>
<div class="age-rating-description">
<h4>年龄分级说明</h4>
<p><?php echo nl2br(htmlspecialchars($app['age_rating_description'])); ?></p>
</div>
<?php endif; ?>
<p><i class="fas fa-laptop"></i> 适用平台: <?php
$platforms = json_decode($app['platforms'], true) ?? [];
$platformIcons = [
'windows' => '<i class="fab fa-windows"></i>',
'windows_win7' => '<i class="fab fa-windows"></i>',
'windows_xp' => '<i class="fab fa-windows"></i>',
'macos' => '<i class="fab fa-apple"></i>',
'linux' => '<i class="fab fa-linux"></i>',
'linux_arch' => '<i class="fab fa-linux"></i>',
'linux_ubuntu' => '<i class="fab fa-linux"></i>',
'android' => '<i class="fab fa-android"></i>',
'ios' => '<i class="fab fa-app-store-ios"></i>'
];
$platformMap = [
'android' => 'Android',
'ios' => 'iOS',
'windows_win7' => 'WindowsWindows 7以上',
'windows_xp' => 'Windows XP',
'macos' => 'MacOS',
'linux_arch' => 'Linux适用于Arch Linux',
'linux_ubuntu' => 'Linux适用于Ubuntu',
'windows' => 'Windows',
'linux' => 'Linux',
];
$platformTexts = [];
foreach ($platforms as $platform) {
$icon = $platformIcons[strtolower($platform)] ?? '';
$readableName = $platformMap[strtolower($platform)] ?? ucfirst($platform);
$platformTexts[] = $icon . ' ' . $readableName;
}
echo implode(', ', $platformTexts);
?></p>
<p><i class="fas fa-star"></i> 评分: <?php echo round($app['avg_rating'], 1); ?>/5</p>
<p>开发者: <?php if ($developerId == 0 || empty($developerName)): ?>管理员<?php else: ?><a href="developer_apps.php?id=<?php echo $developerId; ?>"><?php echo htmlspecialchars($developerName); ?></a><?php endif; ?></p>
<?php
// 获取应用标签
$sqlTags = "SELECT tags.name FROM app_tags JOIN tags ON app_tags.tag_id = tags.id WHERE app_tags.app_id = ?";
$stmtTags = $conn->prepare($sqlTags);
$stmtTags->bind_param("i", $appId);
$stmtTags->execute();
$resultTags = $stmtTags->get_result();
$tags = [];
while ($tag = $resultTags->fetch_assoc()) {
$tags[] = $tag['name'];
}
if (!empty($tags)): ?>
<p><i class="fas fa-tags"></i> 标签: <?php echo implode(', ', $tags); ?></p>
<?php endif; ?>
</div>
<div class="col-md-6">
<div id="imageCarousel" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-inner">
<?php
$first = true;
while ($image = $resultImages->fetch_assoc()) {
$active = $first ? 'active' : '';
echo '<div class="carousel-item '. $active . '">';
echo '<img src="'. $image['image_path'] . '" class="d-block w-100" alt="App Image">';
echo '</div>';
$first = false;
}
?>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#imageCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#imageCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<h2>版本历史</h2>
<?php while ($version = $resultVersions->fetch_assoc()): ?>
<div class="card mb-3 blur-bg">
<div class="card-body">
<h5 class="card-title"><i class="fas fa-code-branch"></i> 版本 <?php echo $version['version']; ?></h5>
<div class="card-text markdown-content"><?php echo htmlspecialchars($version['changelog']); ?></div>
<a href="<?php echo htmlspecialchars($version['file_path']); ?>" class="btn btn-primary btn-lg" download><i class="fas fa-download"></i> 立即下载</a>
<a href="version_list.php?id=<?php echo $app['id']; ?>" class="btn btn-outline-secondary"><i class="fas fa-history"></i> 查看版本历史</a>
</div>
</div>
<?php endwhile; ?>
</div>
</div>
<div class="row mt-4">
<div class="col-md-6">
<h2><i class="fas fa-comments"></i> 评价</h2>
<form method="post" action="">
<div class="mb-3">
<label for="rating" class="form-label"><i class="fas fa-star"></i> 评分</label>
<select class="form-select" id="rating" name="rating" required>
<option value="1">1星</option>
<option value="2">2星</option>
<option value="3">3星</option>
<option value="4">4星</option>
<option value="5">5星</option>
</select>
</div>
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane"></i> 提交评价</button>
</form>
<h2>评价</h2>
<div id="reviews-container">
<?php while ($review = $resultReviews->fetch_assoc()): ?>
<div class="card mb-3 blur-bg">
<div class="card-body">
<?php
$rating = $review['rating'] !== null ? $review['rating'] : 0;
echo '<p class="card-text">评分: ';
for ($i = 1; $i <= 5; $i++) {
if ($i <= floor($rating)) {
echo '<span class="fas fa-star text-warning"></span>';
} elseif ($i - $rating <= 0.5) {
echo '<span class="fas fa-star-half-alt text-warning"></span>';
} else {
echo '<span class="far fa-star text-warning"></span>';
}
}
echo '</p>';
?>
<p class="card-text"><i class="fas fa-clock"></i> <small class="text-muted">评价时间: <?php echo $review['created_at']; ?></small></p>
</div>
</div>
<?php endwhile; ?>
</div>
<?php if ($hasMore): ?>
<button id="load-more" class="btn btn-secondary" data-page="<?php echo $page + 1; ?>">加载更多</button>
<?php endif; ?>
</div>
<div class="col-md-6">
<h2>评分分布</h2>
<div id="ratingChartSkeleton" class="skeleton-chart"></div>
<canvas id="ratingChart" width="400" height="200"></canvas>
<script>
// 加载更多评价功能
document.addEventListener('DOMContentLoaded', function() {
const loadMoreBtn = document.getElementById('load-more');
if (loadMoreBtn) {
loadMoreBtn.addEventListener('click', function() {
const button = this;
const page = parseInt(button.getAttribute('data-page'));
const offset = (page - 1) * 10;
const appId = <?php echo $appId; ?>;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 加载中...';
button.disabled = true;
fetch(`app.php?id=${appId}&offset=${offset}&action=load_reviews`)
.then(response => response.text())
.then(html => {
if (html.trim() === '') {
button.style.display = 'none';
return;
}
document.getElementById('reviews-container').insertAdjacentHTML('beforeend', html);
button.innerHTML = '加载更多';
button.disabled = false;
button.setAttribute('data-page', parseInt(page) + 1);
})
.catch(error => {
console.error('加载评价失败:', error);
button.innerHTML = '加载更多';
button.disabled = false;
});
});
}
});
// 评分图表
const ctx = document.getElementById('ratingChart').getContext('2d');
new Chart(ctx).Bar({
labels: ['5星', '4星', '3星', '2星', '1星'],
datasets: [
// {
// label: '评分数量',
// fillColor: 'rgba(75, 192, 192, 0.6)',
// strokeColor: 'rgba(75, 192, 192, 1)',
// highlightFill: 'rgba(75, 192, 192, 0.8)',
// highlightStroke: 'rgba(75, 192, 192, 1)',
// data: [
// <?php echo $ratingDistribution[5] ?? 0; ?>,
// <?php echo $ratingDistribution[4] ?? 0; ?>,
// <?php echo $ratingDistribution[3] ?? 0; ?>,
// <?php echo $ratingDistribution[2] ?? 0; ?>,
// <?php echo $ratingDistribution[1] ?? 0; ?>
// ]
// },
// {
// label: '评分数量',
// fillColor: 'rgba(153, 102, 255, 0.6)',
// strokeColor: 'rgba(153, 102, 255, 1)',
// highlightFill: 'rgba(153, 102, 255, 0.8)',
// highlightStroke: 'rgba(153, 102, 255, 1)',
// data: [
// <?php echo $ratingDistribution[5] ?? 0; ?>,
// <?php echo $ratingDistribution[4] ?? 0; ?>,
// <?php echo $ratingDistribution[3] ?? 0; ?>,
// <?php echo $ratingDistribution[2] ?? 0; ?>,
// <?php echo $ratingDistribution[1] ?? 0; ?>
// ]
// },
// {
// label: '评分数量',
// fillColor: 'rgba(255, 206, 86, 0.6)',
// strokeColor: 'rgba(255, 206, 86, 1)',
// highlightFill: 'rgba(255, 206, 86, 0.8)',
// highlightStroke: 'rgba(255, 206, 86, 1)',
// data: [
// <?php echo $ratingDistribution[5] ?? 0; ?>,
// <?php echo $ratingDistribution[4] ?? 0; ?>,
// <?php echo $ratingDistribution[3] ?? 0; ?>,
// <?php echo $ratingDistribution[2] ?? 0; ?>,
// <?php echo $ratingDistribution[1] ?? 0; ?>
// ]
// },
// {
// label: '评分数量',
// fillColor: 'rgba(255, 99, 132, 0.6)',
// strokeColor: 'rgba(255, 99, 132, 1)',
// highlightFill: 'rgba(255, 99, 132, 0.8)',
// highlightStroke: 'rgba(255, 99, 132, 1)',
// data: [
// <?php echo $ratingDistribution[5] ?? 0; ?>,
// <?php echo $ratingDistribution[4] ?? 0; ?>,
// <?php echo $ratingDistribution[3] ?? 0; ?>,
// <?php echo $ratingDistribution[2] ?? 0; ?>,
// <?php echo $ratingDistribution[1] ?? 0; ?>
// ]
// },
{
label: '评分数量',
fillColor: 'rgba(54, 162, 235, 0.6)',
strokeColor: 'rgba(54, 162, 235, 1)',
highlightFill: 'rgba(54, 162, 235, 0.8)',
highlightStroke: 'rgba(54, 162, 235, 1)',
data: [
<?php echo $ratingDistribution[5] ?? 0; ?>,
<?php echo $ratingDistribution[4] ?? 0; ?>,
<?php echo $ratingDistribution[3] ?? 0; ?>,
<?php echo $ratingDistribution[2] ?? 0; ?>,
<?php echo $ratingDistribution[1] ?? 0; ?>
]
}
]
}, {
scaleBeginAtZero: true,
scales: {
y: {
beginAtZero: true
}
}
});
document.getElementById('ratingChartSkeleton').style.display = 'none';
</script>
</div>
</div>
</div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
// 解析Markdown内容
document.addEventListener('DOMContentLoaded', function() {
// 解析应用描述
const descriptionElement = document.getElementById('app-description');
if (descriptionElement) {
const markdown = descriptionElement.textContent;
descriptionElement.innerHTML = marked.parse(markdown);
}
// 解析所有版本变更日志
const changelogElements = document.querySelectorAll('.markdown-content');
changelogElements.forEach(function(element) {
const markdown = element.textContent;
element.innerHTML = marked.parse(markdown);
});
});
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
</script>
</body>
</html>

278
app_store.sql Normal file
View File

@@ -0,0 +1,278 @@
-- 创建数据库
-- CREATE DATABASE IF NOT EXISTS app_store;
-- 创建APP表
CREATE TABLE IF NOT EXISTS apps (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
age_rating ENUM('3+', '7+', '12+', '17+') NOT NULL,
age_rating_description TEXT,
platforms JSON NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
version VARCHAR(20) NOT NULL,
changelog TEXT NOT NULL,
file_path VARCHAR(255) NOT NULL,
status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
is_approved TINYINT(1) DEFAULT 0 COMMENT '应用是否已审核',
developer_email VARCHAR(255) NOT NULL
);
-- 确保状态列存在(用于现有数据库)
-- 创建APP版本表
CREATE TABLE IF NOT EXISTS app_versions (
id INT AUTO_INCREMENT PRIMARY KEY,
app_id INT NOT NULL,
version VARCHAR(50) NOT NULL,
changelog TEXT NOT NULL,
file_path VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE
);
-- 创建APP预览图片表
CREATE TABLE IF NOT EXISTS app_images (
id INT AUTO_INCREMENT PRIMARY KEY,
app_id INT NOT NULL,
image_path VARCHAR(255) NOT NULL,
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE
);
-- 创建评价表
CREATE TABLE IF NOT EXISTS reviews (
id INT AUTO_INCREMENT PRIMARY KEY,
app_id INT NOT NULL,
ip_address VARCHAR(45) NOT NULL,
rating TINYINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_review (app_id, ip_address),
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE
);
-- 创建管理员表
CREATE TABLE IF NOT EXISTS admins (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL
);
-- 插入默认管理员
INSERT IGNORE INTO admins (username, password) VALUES ("admin", "your_admin_password_hash");
-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP NULL
);
-- 创建标签表
CREATE TABLE IF NOT EXISTS tags (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建应用标签关联表
CREATE TABLE IF NOT EXISTS app_tags (
app_id INT NOT NULL,
tag_id INT NOT NULL,
PRIMARY KEY (app_id, tag_id),
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
);
-- 创建应用分类表
CREATE TABLE IF NOT EXISTS categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
description TEXT
);
-- 应用与分类的多对多关系表
CREATE TABLE IF NOT EXISTS app_categories (
app_id INT NOT NULL,
category_id INT NOT NULL,
PRIMARY KEY (app_id, category_id),
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE
);
-- 修改评价表,支持文字评论并关联用户
SET @exist_ip_address = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'reviews' AND COLUMN_NAME = 'ip_address');
SET @sql = IF(@exist_ip_address > 0, 'ALTER TABLE reviews DROP COLUMN ip_address', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并删除unique_review索引如果存在
SET @exist_unique_review = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'reviews' AND INDEX_NAME = 'unique_review');
SET @sql = IF(@exist_unique_review > 0, 'ALTER TABLE reviews DROP INDEX unique_review', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加user_id列如果不存在
SET @exist_user_id = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'reviews' AND COLUMN_NAME = 'user_id');
SET @sql = IF(@exist_user_id = 0, 'ALTER TABLE reviews ADD COLUMN user_id INT', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加comment列如果不存在
SET @exist_comment = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'reviews' AND COLUMN_NAME = 'comment');
SET @sql = IF(@exist_comment = 0, 'ALTER TABLE reviews ADD COLUMN comment TEXT', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 添加外键约束(如果不存在)
SET @exist_fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'reviews' AND COLUMN_NAME = 'user_id' AND CONSTRAINT_NAME = 'fk_reviews_users');
SET @sql = IF(@exist_fk = 0, 'ALTER TABLE reviews ADD CONSTRAINT fk_reviews_users FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 检查并添加唯一索引(如果不存在)
SET @exist_unique_index = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'reviews' AND INDEX_NAME = 'unique_user_app_review');
SET @sql = IF(@exist_unique_index = 0, 'ALTER TABLE reviews ADD UNIQUE KEY unique_user_app_review (user_id, app_id)', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 添加应用下载统计
SET @exist_download_count = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'awa' AND TABLE_NAME = 'app_versions' AND COLUMN_NAME = 'download_count');
SET @sql = IF(@exist_download_count = 0, 'ALTER TABLE app_versions ADD COLUMN download_count INT DEFAULT 0', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 创建下载历史表
CREATE TABLE IF NOT EXISTS download_history (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
version_id INT NOT NULL,
download_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (version_id) REFERENCES app_versions(id) ON DELETE CASCADE
);
-- 创建公告表
CREATE TABLE IF NOT EXISTS announcements (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
admin_id INT NOT NULL,
FOREIGN KEY (admin_id) REFERENCES admins(id) ON DELETE CASCADE
);
-- 创建开发者表
CREATE TABLE IF NOT EXISTS developers (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 添加开发者邮箱验证字段(条件性)
SET @exist_verification_token = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'developers' AND COLUMN_NAME = 'verification_token');
SET @sql = IF(@exist_verification_token = 0, 'ALTER TABLE developers ADD COLUMN verification_token VARCHAR(255) NULL', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @exist_is_verified = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'developers' AND COLUMN_NAME = 'is_verified');
SET @sql = IF(@exist_is_verified = 0, 'ALTER TABLE developers ADD COLUMN is_verified BOOLEAN DEFAULT FALSE', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @exist_verified_at = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'developers' AND COLUMN_NAME = 'verified_at');
SET @sql = IF(@exist_verified_at = 0, 'ALTER TABLE developers ADD COLUMN verified_at TIMESTAMP NULL', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 修改 apps 表,添加 developer_id 和 status 字段
SET @exist_developer_id = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'apps' AND COLUMN_NAME = 'developer_id');
SET @sql = IF(@exist_developer_id = 0, 'ALTER TABLE apps ADD COLUMN developer_id INT', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @exist_status = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'apps' AND COLUMN_NAME = 'status');
SET @sql = IF(@exist_status = 0, 'ALTER TABLE apps ADD COLUMN status ENUM(''pending'', ''approved'', ''rejected'') DEFAULT ''pending''', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 添加应用拒绝原因字段
SET @exist_rejection_reason = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'apps' AND COLUMN_NAME = 'rejection_reason');
SET @sql = IF(@exist_rejection_reason = 0, 'ALTER TABLE apps ADD COLUMN rejection_reason TEXT NULL', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @exist_fk = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'apps' AND COLUMN_NAME = 'developer_id' AND CONSTRAINT_NAME = 'fk_apps_developers');
-- 添加 social_links 字段到 developers 表(条件性)
SET @exist_social_links = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'developers' AND COLUMN_NAME = 'social_links');
SET @sql = IF(@exist_social_links = 0, 'ALTER TABLE developers ADD social_links VARCHAR(255) DEFAULT '''' AFTER password', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sql = IF(@exist_fk = 0, 'ALTER TABLE apps ADD CONSTRAINT fk_apps_developers FOREIGN KEY (developer_id) REFERENCES developers(id) ON DELETE SET NULL', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 创建应用推荐表
CREATE TABLE IF NOT EXISTS app_recommendations (
id INT AUTO_INCREMENT PRIMARY KEY,
app_id INT NOT NULL,
reason TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE
);
-- 创建应用更新通知表
CREATE TABLE IF NOT EXISTS app_update_notifications (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
app_id INT NOT NULL,
version_id INT NOT NULL,
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE,
FOREIGN KEY (version_id) REFERENCES app_versions(id) ON DELETE CASCADE
);
-- 创建用户反馈表
CREATE TABLE IF NOT EXISTS user_feedback (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
app_id INT,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE SET NULL
);
-- 修改app_versions表添加最后更新时间戳用于热门排行
SET @exist_last_updated = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'app_versions' AND COLUMN_NAME = 'last_updated');
SET @sql = IF(@exist_last_updated = 0, 'ALTER TABLE app_versions ADD COLUMN last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP', 'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 创建验证令牌索引
CREATE INDEX idx_verification_token ON developers(verification_token);

5
composer.json Normal file
View File

@@ -0,0 +1,5 @@
{
"require": {
"phpmailer/phpmailer": "^6.10"
}
}

100
composer.lock generated Normal file
View File

@@ -0,0 +1,100 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "cb5da62d73716ac8a1776f19367bc28f",
"packages": [
{
"name": "phpmailer/phpmailer",
"version": "v6.10.0",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144",
"reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-filter": "*",
"ext-hash": "*",
"php": ">=5.5.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
"doctrine/annotations": "^1.2.6 || ^1.13.3",
"php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.3.5",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.7.2",
"yoast/phpunit-polyfills": "^1.0.4"
},
"suggest": {
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
"psr/log": "For optional PSR-3 debug logging",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
},
"type": "library",
"autoload": {
"psr-4": {
"PHPMailer\\PHPMailer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-only"
],
"authors": [
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0"
},
"funding": [
{
"url": "https://github.com/Synchro",
"type": "github"
}
],
"time": "2025-04-24T15:19:31+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
}

9
css/all.min.css vendored Normal file

File diff suppressed because one or more lines are too long

12
css/bootstrap.min.css vendored Normal file

File diff suppressed because one or more lines are too long

283
developer/dashboard.php Normal file
View File

@@ -0,0 +1,283 @@
<?php
// 引入配置文件
require_once '../config.php';
session_start();
// 检查开发者是否已登录
if (!isset($_SESSION['developer_id'])) {
header('Location: login.php');
exit;
}
$developerId = $_SESSION['developer_id'];
$developerUsername = $_SESSION['developer_username'];
// 检查数据库连接是否为 MySQLi 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
// 获取开发者的应用列表
$apps = [];
$stmt = $conn->prepare('SELECT id, name, status, rejection_reason FROM apps WHERE developer_id = ?');
if (!$stmt) {
log_error('获取应用列表查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '获取应用列表时发生错误,请稍后再试';
} else {
$stmt->bind_param('i', $developerId);
if (!$stmt->execute()) {
log_error('获取应用列表查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '获取应用列表时发生错误,请稍后再试';
} else {
$result = $stmt->get_result();
$apps = $result->fetch_all(MYSQLI_ASSOC);
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>开发者仪表盘 - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<!-- SweetAlert2 -->
<script src="/js/sweetalert.js"></script>
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
.app-card {
margin-bottom: 1rem;
}
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.dashboard-container {
max-width: 1200px;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
}
.app-list {
margin-top: 20px;
}
.app-item {
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
margin-bottom: 10px;
}
.status-pending {
color: orange;
}
.status-approved {
color: green;
}
.status-rejected {
color: red;
}
.action-buttons {
margin-top: 10px;
}
.action-buttons a {
display: inline-block;
padding: 5px 10px;
background-color: #007BFF;
color: #fff;
text-decoration: none;
border-radius: 3px;
margin-right: 10px;
}
.action-buttons a:hover {
background-color: #0056b3;
}
.add-app {
margin-bottom: 20px;
}
.add-app a {
display: inline-block;
padding: 10px 20px;
background-color: #28a745;
color: #fff;
text-decoration: none;
border-radius: 3px;
}
.add-app a:hover {
background-color: #218838;
}
.logout {
text-align: right;
}
.logout a {
color: #dc3545;
text-decoration: none;
}
.logout a:hover {
text-decoration: underline;
}
</style>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="dashboard.php">应用仪表盘</a>
</li>
<li class="nav-item">
<a class="nav-link" href="upload_app.php">上传应用</a>
</li>
<li class="nav-item">
<a class="nav-link" href="profile.php">更改信息</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="dashboard-container mt-4">
<?php
$rejectedApps = array_filter($apps, function($app) {
return $app['status'] === 'rejected';
});
if (!empty($rejectedApps)):
?>
<div class="alert alert-danger">
<strong>提醒:</strong> 您有 <?php echo count($rejectedApps); ?> 个应用未通过审核,请查看详情。
</div>
<?php endif; ?>
<h1>欢迎,<?php echo htmlspecialchars($developerUsername); ?></h1>
<div class="add-app">
<a href="upload_app.php"><i class="fas fa-upload me-1"></i>上传新应用</a>
</div>
<?php if (isset($error)): ?>
<div style="color: red;"><?php echo $error; ?></div>
<?php endif; ?>
<div class="app-list">
<h2>我的应用</h2>
<?php if (empty($apps)): ?>
<p>您还没有上传任何应用。</p>
<?php else: ?>
<?php foreach ($apps as $app): ?>
<div class="card app-card">
<div class="card-body">
<h5 class="card-title"><?php echo htmlspecialchars($app['name']); ?></h5>
<p class="card-text">
状态:
<?php if ($app['status'] === 'approved'): ?>
<span class="badge bg-success">已通过</span>
<?php elseif ($app['status'] === 'rejected'): ?>
<span class="badge bg-danger">未通过</span>
<div class="alert alert-warning mt-2">
拒绝原因: <?php echo htmlspecialchars($app['rejection_reason']); ?>
</div>
<?php else: ?>
<span class="badge bg-warning">待审核</span>
<?php endif; ?>
</p>
<div class="action-buttons">
<a href="edit_app.php?id=<?php echo $app['id']; ?>" class="btn btn-primary"><i class="fas fa-edit me-1"></i>编辑</a>
<a href="version_control.php?id=<?php echo $app['id']; ?>" class="btn btn-secondary"><i class="fas fa-history me-1"></i>版本控制</a>
<a href="#" class="delete-btn btn btn-danger" data-app-id="<?php echo $app['id']; ?>"><i class="fas fa-trash-alt me-1"></i>删除</a>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<!-- Bootstrap JS and Popper -->
<script src="../js/bootstrap.bundle.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
// 删除按钮事件处理
const deleteButtons = document.querySelectorAll('.delete-btn');
deleteButtons.forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
const appId = this.getAttribute('data-app-id');
const appName = this.closest('.card').querySelector('.card-title').textContent;
Swal.fire({
title: '确认删除',
text: `您确定要删除应用"${appName}"吗?此操作不可撤销。`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: '删除',
cancelButtonText: '取消'
}).then((result) => {
if (result.isConfirmed) {
fetch('delete_app.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `app_id=${encodeURIComponent(appId)}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire('删除成功', data.message, 'success').then(() => {
window.location.reload();
});
} else {
Swal.fire('删除失败', data.message, 'error');
}
})
.catch(error => {
Swal.fire('错误', '删除过程中发生错误', 'error');
});
}
});
});
});
});
</script>
</body>
</html>

81
developer/delete_app.php Normal file
View File

@@ -0,0 +1,81 @@
<?php
// 引入配置文件
require_once '../config.php';
session_start();
// 检查开发者是否已登录
if (!isset($_SESSION['developer_id'])) {
http_response_code(403);
echo json_encode(['success' => false, 'message' => '未授权访问']);
exit;
}
// 验证请求方法和参数
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['app_id'])) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => '无效的请求参数']);
exit;
}
$appId = intval($_POST['app_id']);
$developerId = $_SESSION['developer_id'];
// 检查数据库连接
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
http_response_code(500);
echo json_encode(['success' => false, 'message' => '数据库连接错误']);
exit;
}
// 验证应用所有权
$stmt = $conn->prepare('SELECT id FROM apps WHERE id = ? AND developer_id = ?');
if (!$stmt) {
log_error('验证应用所有权查询准备失败: ' . $conn->error, __FILE__, __LINE__);
http_response_code(500);
echo json_encode(['success' => false, 'message' => '服务器错误']);
exit;
}
$stmt->bind_param('ii', $appId, $developerId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
$stmt->close();
http_response_code(403);
echo json_encode(['success' => false, 'message' => '您没有权限删除此应用']);
exit;
}
$stmt->close();
// 开始事务
$conn->begin_transaction();
try {
// 删除应用的版本记录
$stmt = $conn->prepare('DELETE FROM app_versions WHERE app_id = ?');
if (!$stmt) throw new Exception('删除版本记录准备失败: ' . $conn->error);
$stmt->bind_param('i', $appId);
if (!$stmt->execute()) throw new Exception('删除版本记录执行失败: ' . $stmt->error);
$stmt->close();
// 删除应用记录
$stmt = $conn->prepare('DELETE FROM apps WHERE id = ? AND developer_id = ?');
if (!$stmt) throw new Exception('删除应用记录准备失败: ' . $conn->error);
$stmt->bind_param('ii', $appId, $developerId);
if (!$stmt->execute()) throw new Exception('删除应用记录执行失败: ' . $stmt->error);
$stmt->close();
// 提交事务
$conn->commit();
echo json_encode(['success' => true, 'message' => '应用已成功删除']);
} catch (Exception $e) {
// 回滚事务
$conn->rollback();
log_error('删除应用失败: ' . $e->getMessage(), __FILE__, __LINE__);
http_response_code(500);
echo json_encode(['success' => false, 'message' => '删除应用失败: ' . $e->getMessage()]);
}
?>

479
developer/edit_app.php Normal file
View File

@@ -0,0 +1,479 @@
<?php
// 引入配置文件
require_once '../config.php';
session_start();
// 检查开发者是否已登录
if (!isset($_SESSION['developer_id'])) {
header('Location: login.php');
exit;
}
$developerId = $_SESSION['developer_id'];
$error = '';
$success = '';
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header('Location: dashboard.php');
exit;
}
$appId = $_GET['id'];
$app = [];
// 检查数据库连接是否为 MySQLi 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
header('Location: dashboard.php');
exit;
}
// 获取所有标签
$tags = [];
$tagStmt = $conn->query('SELECT id, name FROM tags');
while ($tag = $tagStmt->fetch_assoc()) {
$tags[] = $tag;
}
$tagStmt->close();
// 获取应用现有标签
$appTags = [];
$appTagStmt = $conn->prepare('SELECT tag_id FROM app_tags WHERE app_id = ?');
$appTagStmt->bind_param('i', $appId);
$appTagStmt->execute();
$appTagResult = $appTagStmt->get_result();
while ($tag = $appTagResult->fetch_assoc()) {
$appTags[] = $tag['tag_id'];
}
$appTagStmt->close();
// 获取应用信息并验证开发者权限
$stmt = $conn->prepare('SELECT id, name, description, version, changelog, age_rating, age_rating_description, platforms, file_path FROM apps WHERE id = ? AND developer_id = ?');
if (!$stmt) {
log_error('获取应用信息查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '获取应用信息时发生错误,请稍后再试';
header('Location: dashboard.php');
exit;
}
$stmt->bind_param('ii', $appId, $developerId);
if (!$stmt->execute()) {
log_error('获取应用信息查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '获取应用信息时发生错误,请稍后再试';
header('Location: dashboard.php');
exit;
}
$result = $stmt->get_result();
$app = $result->fetch_assoc();
if (!$app) {
header('Location: dashboard.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$appName = trim($_POST['name']);
$appDescription = trim($_POST['description']);
$version = trim($_POST['version']);
$changelog = trim($_POST['changelog']);
$ageRating = $_POST['age_rating'];
$ageRatingDescription = trim($_POST['age_rating_description']);
$platforms = $_POST['platforms'] ?? [];
$platforms_json = json_encode($platforms);
$appFilePath = $app['file_path']; // 默认使用现有文件路径
// 获取选中的标签
$selectedTags = $_POST['tags'] ?? [];
// 处理应用文件上传
if (!empty($_FILES['app_file']['tmp_name'])) {
$uploadDir = '../uploads/apps/';
$fileExtension = pathinfo($_FILES['app_file']['name'], PATHINFO_EXTENSION);
$newFileName = uniqid() . '.' . $fileExtension;
$targetPath = $uploadDir . $newFileName;
// 验证文件类型和大小
$allowedTypes = ['apk', 'exe', 'jar', 'crx', 'ini'];
if (!in_array($fileExtension, $allowedTypes)) {
$error = '不支持的文件类型,请上传 ' . implode(', ', $allowedTypes) . ' 格式的文件';
} elseif ($_FILES['app_file']['size'] > 50 * 1024 * 1024) { // 50MB
$error = '文件大小不能超过50MB';
} elseif (!move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
$error = '文件上传失败,请检查服务器权限';
} else {
// 删除旧文件
if (file_exists($appFilePath)) {
unlink($appFilePath);
}
$appFilePath = $targetPath;
}
}
// 处理图片删除
if (!empty($_POST['removed_images'])) {
$removedImageIds = explode(',', $_POST['removed_images']);
foreach ($removedImageIds as $imgId) {
if (is_numeric($imgId)) {
// 获取图片路径
$stmt = $conn->prepare("SELECT image_path FROM app_images WHERE id = ?");
$stmt->bind_param("i", $imgId);
$stmt->execute();
$imgResult = $stmt->get_result();
if ($img = $imgResult->fetch_assoc()) {
// 删除文件
if (file_exists($img['image_path'])) {
unlink($img['image_path']);
}
// 删除数据库记录
$deleteStmt = $conn->prepare("DELETE FROM app_images WHERE id = ?");
$deleteStmt->bind_param("i", $imgId);
$deleteStmt->execute();
$deleteStmt->close();
}
$stmt->close();
}
}
}
// 更新应用标签
// 删除现有标签关联
$deleteTagStmt = $conn->prepare('DELETE FROM app_tags WHERE app_id = ?');
$deleteTagStmt->bind_param('i', $appId);
$deleteTagStmt->execute();
$deleteTagStmt->close();
// 添加新标签关联
foreach ($selectedTags as $tagId) {
if (is_numeric($tagId)) {
$tagStmt = $conn->prepare('INSERT INTO app_tags (app_id, tag_id) VALUES (?, ?)');
$tagStmt->bind_param('ii', $appId, $tagId);
$tagStmt->execute();
$tagStmt->close();
}
}
// 处理新图片上传
$imageUploadDir = '../uploads/images/';
$allowedImageTypes = ['jpg', 'jpeg', 'png'];
$maxImages = 5;
$currentImageCount = count($existingImages) - count($removedImageIds ?? []);
if (!empty($_FILES['images']['name'][0]) && empty($error)) {
$newImages = $_FILES['images'];
for ($i = 0; $i < count($newImages['name']); $i++) {
if ($newImages['error'][$i] !== UPLOAD_ERR_OK) continue;
$fileName = $newImages['name'][$i];
$fileTmp = $newImages['tmp_name'][$i];
$fileSize = $newImages['size'][$i];
$fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if (!in_array($fileExt, $allowedImageTypes)) {
$error = "图片 {$fileName} 格式不支持仅允许jpg、png";
break;
}
if ($fileSize > 2 * 1024 * 1024) { // 2MB
$error = "图片 {$fileName} 大小超过2MB";
break;
}
if ($currentImageCount >= $maxImages) {
$error = "最多只能上传5张图片";
break;
}
$newFileName = uniqid() . '.' . $fileExt;
$targetPath = $imageUploadDir . $newFileName;
if (move_uploaded_file($fileTmp, $targetPath)) {
// 插入数据库
$stmt = $conn->prepare("INSERT INTO app_images (app_id, image_path) VALUES (?, ?)");
$stmt->bind_param("is", $appId, $targetPath);
$stmt->execute();
$stmt->close();
$currentImageCount++;
} else {
$error = "图片 {$fileName} 上传失败";
break;
}
}
}
// 验证标签选择
if (empty($selectedTags)) {
$error = '至少需要选择一个应用标签';
}
if (empty($appName) || empty($appDescription) || empty($version) || empty($changelog) || empty($ageRating) || empty($ageRatingDescription)) {
$error = '应用名称和描述不能为空';
} else {
// 检查数据库连接是否为 MySQLi 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
$platforms = $_POST['platforms'] ?? [];
$platforms_json = json_encode($platforms);
$stmt = $conn->prepare('UPDATE apps SET name = ?, description = ?, version = ?, changelog = ?, age_rating = ?, age_rating_description = ?, platforms = ?, file_path = ?, status = \'pending\' WHERE id = ? AND developer_id = ?');
if (!$stmt) {
log_error('更新应用信息查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '更新应用信息时发生错误,请稍后再试';
} else {
$stmt->bind_param('ssssssssii', $appName, $appDescription, $version, $changelog, $ageRating, $ageRatingDescription, $platforms_json, $appFilePath, $appId, $developerId);
if (!$stmt->execute()) {
log_error('更新应用信息查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '更新应用信息时发生错误,请稍后再试';
} else {
$success = '应用信息更新成功,请等待管理员重新审核';
header('Location: dashboard.php?success=' . urlencode($success));
exit;
$app['name'] = $appName;
$app['description'] = $appDescription;
}
}
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>编辑应用</title>
<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
</head>
<body>
<div class="container mt-4">
<h2 class="text-center mb-4">编辑应用</h2>
<?php if (!empty($error)): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<?php if (!empty($success)): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<form method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="name" class="form-label">应用名称</label>
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($app['name']); ?>" required>
</div>
<div class="mb-3">
<label for="description" class="form-label">应用描述</label>
<textarea class="form-control" id="description" name="description" rows="5" required><?php echo htmlspecialchars($app['description']); ?></textarea>
</div>
<div class="mb-3">
<label for="version" class="form-label">版本号</label>
<input type="text" class="form-control" id="version" name="version" value="<?php echo htmlspecialchars($app['version']); ?>" required>
</div>
<div class="mb-3">
<label for="changelog" class="form-label">更新日志</label>
<textarea class="form-control" id="changelog" name="changelog" rows="3" required><?php echo htmlspecialchars($app['changelog']); ?></textarea>
</div>
<div class="mb-3">
<label for="age_rating" class="form-label">年龄分级</label>
<select class="form-select" id="age_rating" name="age_rating" required>
<option value="3+" <?php echo $app['age_rating'] === '3+' ? 'selected' : ''; ?>>3+</option>
<option value="7+" <?php echo $app['age_rating'] === '7+' ? 'selected' : ''; ?>>7+</option>
<option value="12+" <?php echo $app['age_rating'] === '12+' ? 'selected' : ''; ?>>12+</option>
<option value="17+" <?php echo $app['age_rating'] === '17+' ? 'selected' : ''; ?>>17+</option>
</select>
</div>
<div class="mb-3">
<label for="age_rating_description" class="form-label">年龄分级说明</label>
<input type="text" class="form-control" id="age_rating_description" name="age_rating_description" value="<?php echo htmlspecialchars($app['age_rating_description']); ?>" required>
</div>
<div class="mb-3">
<label class="form-label">适用平台</label>
<?php $platforms = json_decode($app['platforms'], true) ?? [];
// 解析平台值,提取主平台和子选项
$mainPlatforms = [];
$subOptions = [];
foreach($platforms as $platform){
if(strpos($platform, 'windows_') === 0){
$mainPlatforms[] = 'windows';
$subOptions['windows'] = $platform;
} elseif(strpos($platform, 'linux_') === 0){
$mainPlatforms[] = 'linux';
$subOptions['linux'] = $platform;
} else{
$mainPlatforms[] = $platform;
}
}
$mainPlatforms = array_unique($mainPlatforms);
?>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="windows" id="windows" name="platforms[]" <?php echo in_array('windows', $mainPlatforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="windows">Windows</label>
</div>
<div id="windows_suboptions" class="ms-4 mt-2" style="display: <?php echo in_array('windows', $mainPlatforms) ? 'block' : 'none'; ?>">
<div class="form-check">
<input class="form-check-input" type="radio" name="windows_version" id="windows_xp" value="windows_xp" <?php echo isset($subOptions['windows']) && $subOptions['windows'] === 'windows_xp' ? 'checked' : ''; ?>>
<label class="form-check-label" for="windows_xp">XP以前</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="windows_version" id="windows_win7" value="windows_win7" <?php echo isset($subOptions['windows']) && $subOptions['windows'] === 'windows_win7' ? 'checked' : ''; ?>>
<label class="form-check-label" for="windows_win7">Win7以后</label>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="macos" id="macos" name="platforms[]" <?php echo in_array('macos', $mainPlatforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="macos">macOS</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="linux" id="linux" name="platforms[]" <?php echo in_array('linux', $mainPlatforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="linux">Linux</label>
</div>
<div id="linux_suboptions" class="ms-4 mt-2" style="display: <?php echo in_array('linux', $mainPlatforms) ? 'block' : 'none'; ?>">
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_ubuntu" value="linux_ubuntu" <?php echo isset($subOptions['linux']) && $subOptions['linux'] === 'linux_ubuntu' ? 'checked' : ''; ?>>
<label class="form-check-label" for="linux_ubuntu">Ubuntu</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_arch" value="linux_arch" <?php echo isset($subOptions['linux']) && $subOptions['linux'] === 'linux_arch' ? 'checked' : ''; ?>>
<label class="form-check-label" for="linux_arch">Arch Linux</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_centos" value="linux_centos" <?php echo isset($subOptions['linux']) && $subOptions['linux'] === 'linux_centos' ? 'checked' : ''; ?>>
<label class="form-check-label" for="linux_centos">CentOS</label>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="platform_android" name="platforms[]" value="Android" <?php echo in_array('Android', $mainPlatforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="platform_android">Android</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="platform_ios" name="platforms[]" value="iOS" <?php echo in_array('iOS', $mainPlatforms) ? 'checked' : ''; ?>>
<label class="form-check-label" for="platform_ios">iOS</label>
</div>
</div>
<div class="mb-3">
<label for="tags" class="form-label">应用标签 (至少选择1个)</label>
<select id="tags" name="tags[]" multiple class="form-control">
<?php foreach ($tags as $tag): ?>
<option value="<?php echo $tag['id']; ?>" <?php echo in_array($tag['id'], $appTags) ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($tag['name']); ?>
</option>
<?php endforeach; ?>
</select>
<small class="form-text text-muted">按住Ctrl键可选择多个标签</small>
</div>
<div class="mb-3">
<label for="app_file" class="form-label">更新应用文件</label>
<input class="form-control" type="file" id="app_file" name="app_file">
<div class="form-text">当前文件: <?php echo basename($app['file_path']); ?></div>
</div>
<div class="mb-3">
<label class="form-label">应用图片 (最多5张)</label>
<?php
// 获取现有图片
$existingImages = [];
$stmt = $conn->prepare("SELECT id, image_path FROM app_images WHERE app_id = ?");
$stmt->bind_param("i", $appId);
$stmt->execute();
$imgResult = $stmt->get_result();
while ($img = $imgResult->fetch_assoc()) {
$existingImages[] = $img;
}
$stmt->close();
?>
<!-- 现有图片 -->
<?php if (!empty($existingImages)): ?>
<div class="mb-3">
<label>现有图片:</label>
<div class="d-flex flex-wrap gap-2">
<?php foreach ($existingImages as $img): ?>
<div class="position-relative">
<img src="<?php echo htmlspecialchars($img['image_path']); ?>" alt="应用图片" style="width: 100px; height: 100px; object-fit: cover; border-radius: 4px;">
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0" onclick="removeImage(<?php echo $img['id']; ?>)">×</button>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<!-- 新图片上传 -->
<input type="file" name="images[]" multiple accept="image/*" class="form-control">
<small>支持jpg、png格式最多上传5张图片</small>
</div>
<input type="hidden" name="removed_images" id="removed_images" value="">
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-save me-1"></i>保存更改</button>
</form>
<div class="text-center mt-3">
<a href="dashboard.php" class="btn btn-secondary">返回仪表盘</a>
</div>
</div>
<script>
function removeImage(imageId) {
const removedInput = document.getElementById('removed_images');
const currentValues = removedInput.value ? removedInput.value.split(',') : [];
if (!currentValues.includes(imageId.toString())) {
currentValues.push(imageId);
removedInput.value = currentValues.join(',');
}
// 从DOM中移除图片元素
event.target.closest('.position-relative').remove();
}
// 平台子选项显示控制
document.getElementById('windows').addEventListener('change', function() {
const suboptions = document.getElementById('windows_suboptions');
suboptions.style.display = this.checked ? 'block' : 'none';
if (!this.checked) {
document.querySelectorAll('input["windows_version"]').forEach(radio => radio.checked = false);
}
});
document.getElementById('linux').addEventListener('change', function() {
const suboptions = document.getElementById('linux_suboptions');
suboptions.style.display = this.checked ? 'block' : 'none';
if (!this.checked) {
document.querySelectorAll('input[name="linux_distribution"]').forEach(radio => radio.checked = false);
}
});
// 表单提交验证
document.querySelector('form').addEventListener('submit', function(e) {
// 验证Windows子选项
if (document.getElementById('windows').checked && !document.querySelector('input[name="windows_version"]:checked')) {
e.preventDefault();
Swal.fire({
title: '提示',
text: '请选择Windows版本XP以前或Win7以后',
icon: 'warning',
confirmButtonText: '确定'
});
return;
}
// 验证Linux子选项
if (document.getElementById('linux').checked && !document.querySelector('input[name="linux_distribution"]:checked')) {
e.preventDefault();
Swal.fire({
title: '提示',
text: '请选择Linux发行版Ubuntu、Arch Linux或CentOS',
icon: 'warning',
confirmButtonText: '确定'
});
return;
}
// 更新平台值包含子选项信息
const platforms = [];
if (document.getElementById('android').checked) platforms.push('Android');
if (document.getElementById('ios').checked) platforms.push('iOS');
if (document.getElementById('macos').checked) platforms.push('macos');
if (document.getElementById('windows').checked) {
platforms.push(document.querySelector('input[name="windows_version"]:checked').value);
}
if (document.getElementById('linux').checked) {
platforms.push(document.querySelector('input[name="linux_distribution"]:checked').value);
}
// 设置隐藏字段值
document.getElementById('platforms_hidden').value = JSON.stringify(platforms);
});
</script>
</body>
</html>

166
developer/login.php Normal file
View File

@@ -0,0 +1,166 @@
<?php
// 引入配置文件
require_once '../config.php';
// 顶栏样式
echo '<style>
.navbar.scrolled {
background-color: rgba(255, 255, 255, 0.95) !important;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
</style>';
// 导航栏
echo '<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php">'. APP_STORE_NAME . '</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="../index.php">首页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="register.php">开发者注册</a>
</li>
</ul>
</div>
</div>
</nav>';
// 为内容添加顶部内边距
echo '<div style="padding-top: 70px;">';
session_start();
$error = '';
if (isset($_GET['register_success']) && $_GET['register_success'] == 1) {
$success = '注册成功,请登录';
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$loginId = trim($_POST['login_id']);
$password = $_POST['password'];
if (empty($loginId) || empty($password)) {
$error = '邮箱/用户名和密码不能为空';
} else {
// 检查数据库连接是否为 MySQLi 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
$stmt = $conn->prepare('SELECT id, username, password FROM developers WHERE email = ? OR username = ?');
if (!$stmt) {
log_error('登录查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '登录时发生错误,请稍后再试';
} else {
$stmt->bind_param('ss', $loginId, $loginId);
if (!$stmt->execute()) {
log_error('登录查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '登录时发生错误,请稍后再试';
} else {
$result = $stmt->get_result();
$developer = $result->fetch_assoc();
if ($developer && password_verify($password, $developer['password'])) {
$_SESSION['developer_id'] = $developer['id'];
$_SESSION['developer_username'] = $developer['username'];
// 处理自动登录
if (isset($_POST['remember_me']) && $_POST['remember_me'] === 'on') {
$cookie_lifetime = 30 * 24 * 60 * 60; // 30天
$cookie_params = session_get_cookie_params();
setcookie(
session_name(),
session_id(),
time() + $cookie_lifetime,
$cookie_params['path'],
$cookie_params['domain'],
$cookie_params['secure'],
$cookie_params['httponly']
);
ini_set('session.gc_maxlifetime', $cookie_lifetime);
}
header('Location: dashboard.php');
exit;
} else {
$error = '邮箱/用户名或密码错误';
}
}
}
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>开发者登录</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<style>
body {
background-color: #f4f4f4;
padding: 20px 0;
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body class="page-transition">
<div class="container mt-5 col-md-4">
<h2>开发者登录</h2>
<?php if (isset($success)): ?>
<div class="alert alert-success" role="alert"><?php echo $success; ?></div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert alert-danger" role="alert"><?php echo $error; ?></div>
<?php endif; ?>
<form method="post">
<div class="form-floating mb-3">
<input type="text" id="login_id" name="login_id" class="form-control" placeholder="请输入邮箱或用户名" required>
<label for="login_id">邮箱/用户名</label>
</div>
<div class="form-floating mb-3">
<input type="password" id="password" name="password" class="form-control" placeholder="请输入密码" required>
<label for="password">密码</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="remember_me" id="remember_me">
<label class="form-check-label" for="remember_me">
自动登录
</label>
</div>
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-user me-1"></i>登录</button>
</form>
<div class="text-center mt-3">
还没有账号?<a href="register.php" class="text-decoration-none">注册</a>
</div>
</div>
<script src="/js/bootstrap.bundle.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
</body>
</html>

10
developer/logout.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
session_start();
// 销毁会话数据
session_unset();
session_destroy();
// 重定向到登录页面
header('Location: login.php');
exit;

207
developer/profile.php Normal file
View File

@@ -0,0 +1,207 @@
<?php
// 引入配置文件
require_once '../config.php';
session_start();
// 检查开发者是否已登录
if (!isset($_SESSION['developer_id'])) {
header('Location: login.php');
exit;
}
$developerId = $_SESSION['developer_id'];
$error = '';
$success = '';
// 检查数据库连接是否为 MySQLi 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
// 获取开发者信息
$stmt = $conn->prepare('SELECT username, email, social_links FROM developers WHERE id = ?');
if (!$stmt) {
log_error('获取开发者信息查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '获取开发者信息时发生错误,请稍后再试';
} else {
$stmt->bind_param('i', $developerId);
if (!$stmt->execute()) {
log_error('获取开发者信息查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '获取开发者信息时发生错误,请稍后再试';
} else {
$result = $stmt->get_result();
$developer = $result->fetch_assoc();
}
}
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$newUsername = trim($_POST['username']);
$newEmail = trim($_POST['email']);
$newPassword = $_POST['password'];
$newSocialLinks = trim($_POST['social_links']);
// 更新用户名和邮箱
$stmt = $conn->prepare('UPDATE developers SET username = ?, email = ?, social_links = ? WHERE id = ?');
if (!$stmt) {
log_error('更新开发者信息查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '更新信息时发生错误,请稍后再试';
} else {
$stmt->bind_param('sssi', $newUsername, $newEmail, $newSocialLinks, $developerId);
if (!$stmt->execute()) {
log_error('更新开发者信息查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '更新信息时发生错误,请稍后再试';
} else {
// 更新密码
if (!empty($newPassword)) {
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
$stmt = $conn->prepare('UPDATE developers SET password = ? WHERE id = ?');
if (!$stmt) {
log_error('更新密码查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '更新密码时发生错误,请稍后再试';
} else {
$stmt->bind_param('si', $hashedPassword, $developerId);
if (!$stmt->execute()) {
log_error('更新密码查询执行失败: ' . $stmt->error, __FILE__, __LINE__);
$error = '更新密码时发生错误,请稍后再试';
}
}
}
if (empty($error)) {
$success = '信息更新成功';
$_SESSION['developer_username'] = $newUsername;
// 重新获取开发者信息
$stmt = $conn->prepare('SELECT username, email, social_links FROM developers WHERE id = ?');
if ($stmt) {
$stmt->bind_param('i', $developerId);
if ($stmt->execute()) {
$result = $stmt->get_result();
$developer = $result->fetch_assoc();
}
}
}
}
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>开发者信息 - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.profile-container {
max-width: 800px;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
text-align: center;
}
.form-group {
margin-bottom: 1rem;
}
.error {
color: red;
}
.success {
color: green;
}
</style>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="dashboard.php">应用仪表盘</a>
</li>
<li class="nav-item">
<a class="nav-link" href="upload_app.php">上传应用</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">退出登录</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="profile.php">开发者信息</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="profile-container mt-4">
<?php if (!empty($error)): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<?php if (!empty($success)): ?>
<div class="success"><?php echo $success; ?></div>
<?php endif; ?>
<h1>开发者信息</h1>
<form method="post">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" name="username" value="<?php echo htmlspecialchars($developer['username']); ?>" placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" class="form-control" id="email" name="email" value="<?php echo htmlspecialchars($developer['email']); ?>" required>
</div>
<div class="form-group">
<label for="password">新密码 (留空则不修改)</label>
<input type="password" class="form-control" id="password" name="password">
</div>
<div class="form-group">
<label for="social_links">社交媒体链接 (多个链接用逗号分隔)</label>
<input type="text" class="form-control" id="social_links" name="social_links" value="<?php echo htmlspecialchars($developer['social_links']); ?>" placeholder="请输入社交媒体链接,多个链接用逗号分隔">
</div>
<button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>保存更改</button>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
</body>
</html>

232
developer/register.php Normal file
View File

@@ -0,0 +1,232 @@
<?php
// 引入配置文件
// 检查配置文件是否存在并加载
$configFile = '/www/wwwroot/leonmmcoset.jjxmm.win_8010/config.php';
if (!file_exists($configFile)) {
die('配置文件缺失: ' . $configFile . ',无法继续执行');
}
require_once $configFile;
// 引入日志工具
require_once '/www/wwwroot/leonmmcoset.jjxmm.win_8010/includes/logger.php';
// 配置文件加载后日志记录和常量检查
log_error('配置文件已成功加载: ' . $configFile);
// 验证关键常量是否定义
log_error('配置加载后常量检查 - SMTP_HOST: ' . (defined('SMTP_HOST') ? SMTP_HOST : '未定义'));
log_error('配置加载后常量检查 - SMTP_USERNAME: ' . (defined('SMTP_USERNAME') ? SMTP_USERNAME : '未定义'));
log_error('配置加载后常量检查 - SMTP_PASSWORD: ' . (defined('SMTP_PASSWORD') ? '已设置' : '未定义'));
log_error('配置加载后常量检查 - SMTP_PORT: ' . (defined('SMTP_PORT') ? SMTP_PORT : '未定义'));
log_error('配置文件加载后 - SMTP_USERNAME: ' . (defined('SMTP_USERNAME') ? SMTP_USERNAME : '未定义') . ', SMTP_PORT: ' . (defined('SMTP_PORT') ? SMTP_PORT : '未定义'));
// 引入PHPMailer命名空间
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
// 引入Composer自动加载器
require_once '../vendor/autoload.php';
// 顶栏样式
echo '<style>
.navbar.scrolled {
background-color: rgba(255, 255, 255, 0.95) !important;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
</style>';
// 导航栏
echo '<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php">'. APP_STORE_NAME . '</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="../index.php">首页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="login.php">开发者登录</a>
</li>
</ul>
</div>
</div>
</nav>';
// 为内容添加顶部内边距
echo '<div style="padding-top: 70px;">';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
if (empty($username) || empty($email) || empty($password)) {
$error = '用户名、邮箱和密码不能为空';
} elseif (empty($_POST['agree'])) {
$error = '必须同意 APP 审核标准才能注册';
} elseif (empty($_POST['agree_privacy'])) {
$error = '必须同意隐私政策才能注册';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = '请输入有效的邮箱地址';
} else {
// 检查数据库连接是否为 PDO 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
try {
$stmt = $conn->prepare('SELECT id FROM developers WHERE username = ? OR email = ?');
$stmt->bind_param('ss', $username, $email);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows > 0) {
$error = '用户名或邮箱已被注册';
} else {
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 生成验证令牌
$verificationToken = bin2hex(random_bytes(32));
$insertStmt = $conn->prepare('INSERT INTO developers (username, email, password, verification_token) VALUES (?, ?, ?, ?)');
$insertStmt->bind_param('ssss', $username, $email, $hashedPassword, $verificationToken);
if (!$insertStmt->execute()) {
log_error('插入执行失败: ' . $insertStmt->error, __FILE__, __LINE__);
$error = '系统错误,请稍后再试';
} else {
// 生成验证链接
$verificationLink = 'http://' . $_SERVER['HTTP_HOST'] . '/developer/verify_email.php?token=' . urlencode($verificationToken);
// 加载邮件模板
$templatePath = __DIR__ . '/../mail/verification_template.php';
if (file_exists($templatePath)) {
$templateContent = file_get_contents($templatePath);
$templateContent = str_replace('{username}', htmlspecialchars($username), $templateContent);
$templateContent = str_replace('{verification_link}', $verificationLink, $templateContent);
// 调试日志测试
$testLogDir = 'c:\\web\\app2\\logs';
$testLogFile = $testLogDir . '\\test.log';
if (!is_dir($testLogDir)) {
mkdir($testLogDir, 0755, true);
}
file_put_contents($testLogFile, date('[Y-m-d H:i:s] ') . '邮件发送代码开始执行' . PHP_EOL, FILE_APPEND);
// 添加SMTP配置调试日志
log_error('SMTP配置参数 - HOST: ' . (defined('SMTP_HOST') ? SMTP_HOST : '未定义') . ', PORT: ' . (defined('SMTP_PORT') ? SMTP_PORT : '未定义') . ', USERNAME: ' . (defined('SMTP_USERNAME') ? SMTP_USERNAME : '未定义') . ', ENCRYPTION: ' . (defined('SMTP_ENCRYPTION') ? SMTP_ENCRYPTION : '未定义'), __FILE__, __LINE__);
log_error('开始执行邮件发送流程', __FILE__, __LINE__);
// 配置SMTP邮件发送
require_once '../vendor/phpmailer/phpmailer/src/PHPMailer.php';
require_once '../vendor/phpmailer/phpmailer/src/SMTP.php';
/** @var \PHPMailer\PHPMailer\PHPMailer $mail */
$mail = new \PHPMailer\PHPMailer\PHPMailer(true);
try {
$mail->isSMTP();
$mail->SMTPDebug = 4;
// 输出当前SMTP配置参数用于调试
log_error('SMTP配置参数: HOST=' . SMTP_HOST . ', PORT=' . SMTP_PORT . ', USERNAME=' . SMTP_USERNAME . ', ENCRYPTION=' . SMTP_ENCRYPTION);
// 检查openssl扩展是否启用
log_error('OpenSSL扩展状态: ' . (extension_loaded('openssl') ? '已启用' : '未启用')); // 启用详细调试
$mail->Debugoutput = function($str, $level) {
$logDir = 'c:\\web\\app2\\logs';
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
file_put_contents($logDir . '\\smtp_debug.log', date('[Y-m-d H:i:s] ') . $str . PHP_EOL, FILE_APPEND);
};
$mail->Host = defined('SMTP_HOST') ? SMTP_HOST : 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = defined('SMTP_USERNAME') ? SMTP_USERNAME : ''; // Ensure SMTP_USERNAME is defined in config.php
$mail->Password = defined('SMTP_PASSWORD') ? SMTP_PASSWORD : '';
$mail->SMTPSecure = defined('SMTP_ENCRYPTION') ? SMTP_ENCRYPTION : 'tls'; // Ensure SMTP_ENCRYPTION is defined in config.php
$mail->AuthType = 'PLAIN'; // 尝试使用PLAIN认证方式
$mail->Port = defined('SMTP_PORT') ? SMTP_PORT : 587;
$mail->CharSet = 'UTF-8';
$mail->setFrom(defined('SMTP_FROM_EMAIL') ? SMTP_FROM_EMAIL : 'noreply@example.com', defined('SMTP_FROM_NAME') ? SMTP_FROM_NAME : 'App Store'); // Ensure SMTP_FROM_EMAIL is defined in config.php
$mail->addAddress($email, $username);
$mail->isHTML(true);
$mail->Subject = '邮箱验证 - ' . (defined('APP_STORE_NAME') ? APP_STORE_NAME : 'App Store');
$mail->Body = $templateContent;
$mail->send();
} catch (\PHPMailer\PHPMailer\Exception $e) {
log_error('邮件发送失败: ' . $mail->ErrorInfo, __FILE__, __LINE__);
}
} else {
log_error('验证邮件模板不存在: ' . $templatePath, __FILE__, __LINE__);
}
header('Location: login.php?register_success=1&verify_email_sent=1');
exit;
}
}
} catch (PDOException $e) {
$error = '注册时发生错误,请稍后再试';
}
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>开发者注册</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<style>
body {
background-color: #f4f4f4;
padding: 20px 0;
}
</style>
</head>
<body>
<div class="container mt-5 col-md-4">
<h2>开发者注册</h2>
<?php if (!empty($error)): ?>
<div class="alert alert-danger" role="alert"><?php echo $error; ?></div>
<?php endif; ?>
<form method="post">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="username" name="username" placeholder="用户名" required>
<label for="username">用户名</label>
</div>
<div class="form-floating mb-3">
<input type="email" class="form-control" id="email" name="email" placeholder="邮箱" required>
<label for="email">邮箱</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="password" name="password" placeholder="密码" required>
<label for="password">密码</label>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="agree" name="agree" required>
<label class="form-check-label" for="agree">我已阅读并同意 <a href="/docs/app_review_standards.php" target="_blank">APP 审核标准</a></label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="privacy_policy" name="agree_privacy">
<label class="form-check-label" for="privacy_policy">
我已阅读并同意 <a href="../docs/privacy_policy.php" target="_blank">隐私政策</a>
</label>
</div>
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-user-plus me-1"></i>注册</button>
</form>
</div>
<script src="/js/bootstrap.bundle.js"></script>
</body>
</html>

589
developer/upload_app.php Normal file
View File

@@ -0,0 +1,589 @@
<?php
require_once('../includes/logger.php');
set_time_limit(0); // 添加此行取消脚本超时限制
// 引入配置文件
require_once '../config.php';
session_start();
// 检查开发者是否已登录
if (!isset($_SESSION['developer_id']) || !is_numeric($_SESSION['developer_id'])) {
log_error('开发者会话ID不存在或无效', __FILE__, __LINE__);
header('Location: login.php');
exit;
}
$developerId = (int)$_SESSION['developer_id'];
log_info("上传应用的开发者ID: $developerId", __FILE__, __LINE__);
log_info("上传应用的开发者ID: $developerId", __FILE__, __LINE__);
$error = '';
$success = '';
// 检查开发者邮箱是否已验证
$stmt = $conn->prepare('SELECT is_verified FROM developers WHERE id = ?');
if (!$stmt) {
log_error('准备验证状态查询失败: ' . $conn->error, __FILE__, __LINE__);
$error = '系统错误,请稍后再试';
} else {
$stmt->bind_param('i', $developerId);
$stmt->execute();
$result = $stmt->get_result();
$developer = $result->fetch_assoc();
$stmt->close();
log_info("开发者验证状态: " . ($developer ? ($developer['is_verified'] ? "已验证" : "未验证") : "开发者不存在"), __FILE__, __LINE__);
if (!$developer) {
$error = '开发者账号不存在,请重新登录。';
} elseif (!$developer['is_verified']) {
$error = '您的邮箱尚未验证,请先验证邮箱后再上传应用。';
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 创建上传目录(如果不存在)
$uploadDirs = ['../uploads/apps', '../uploads/images'];
foreach ($uploadDirs as $dir) {
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
}
// 获取表单数据
$appName = trim($_POST['name']);
$appDescription = trim($_POST['description']);
$tags = $_POST['tags'] ?? [];
$ageRating = $_POST['age_rating'] ?? '';
$ageRatingDescription = $_POST['age_rating_description'] ?? '';
$platforms = isset($_POST['platforms']) ? $_POST['platforms'] : [];
$version = trim($_POST['version']);
$changelog = trim($_POST['changelog']);
// 验证表单数据
if (empty($appName) || empty($appDescription)) {
$error = '应用名称和描述不能为空';
} elseif (empty($changelog)) {
$error = '更新日志不能为空';
} elseif (empty($platforms)) {
$error = '请至少选择一个适用平台';
} elseif (in_array($ageRating, ['12+', '17+']) && empty($ageRatingDescription)) {
$error = '年龄分级为12+或以上时,必须提供年龄分级说明';
} else {
// 检查数据库连接是否为 MySQLi 对象
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请检查配置';
} else {
// 处理应用文件上传
// 获取选中的平台
$selectedPlatforms = $_POST['platforms'] ?? [];
// 处理应用文件上传
$appFile = $_FILES['app_file'] ?? null;
$appFilePath = '';
if ($appFile && $appFile['error'] === UPLOAD_ERR_OK) {
// 验证文件大小 (100MB)
if ($appFile['size'] > 500 * 1024 * 1024) {
log_error('应用文件过大: ' . number_format($appFile['size'] / 1024 / 1024, 2) . 'MB', __FILE__, __LINE__);
$error = '应用文件大小不能超过500MB';
}
$appExtension = pathinfo($appFile['name'], PATHINFO_EXTENSION);
$appFileName = uniqid() . '.' . $appExtension;
$appRelativePath = 'uploads/apps/' . $appFileName;
$appFilePath = __DIR__ . '/../' . $appRelativePath;
if (!move_uploaded_file($appFile['tmp_name'], $appFilePath)) {
log_error('应用文件移动失败', __FILE__, __LINE__);
$error = '应用文件上传失败';
}
} else {
// 验证标签ID是否存在
if (!empty($tags)) {
$tagIds = implode(',', array_map('intval', $tags));
$tagCheckStmt = $conn->prepare("SELECT id FROM tags WHERE id IN ($tagIds)");
if (!$tagCheckStmt) {
log_error('标签验证查询准备失败: ' . $conn->error, __FILE__, __LINE__);
$error = '系统错误,请稍后再试';
} else {
$tagCheckStmt->execute();
$tagResult = $tagCheckStmt->get_result();
$validTagIds = [];
while ($tag = $tagResult->fetch_assoc()) {
$validTagIds[] = $tag['id'];
}
$tagCheckStmt->close();
$invalidTags = array_diff($tags, $validTagIds);
if (!empty($invalidTags)) {
log_error('无效的标签ID: ' . implode(',', $invalidTags), __FILE__, __LINE__);
$error = '选择了无效的标签,请刷新页面重试';
}
}
}
$error = '应用文件上传错误: ' . ($appFile ? $appFile['error'] : '未找到文件');
}
// 处理图片上传
$imagePaths = [];
$images = $_FILES['images'] ?? null;
if ($images && is_array($images['tmp_name'])) {
foreach ($images['tmp_name'] as $key => $tmpName) {
if ($images['error'][$key] === UPLOAD_ERR_OK) {
// 验证图片大小 (10MB)
if ($images['size'][$key] > 10 * 1024 * 1024) {
log_error('图片过大: ' . $images['name'][$key] . ' (' . number_format($images['size'][$key] / 1024 / 1024, 2) . 'MB)', __FILE__, __LINE__);
$error = '图片 ' . $images['name'][$key] . ' 大小不能超过10MB';
}
$imageRelativePath = 'uploads/images/' . uniqid() . '.' . pathinfo($images['name'][$key], PATHINFO_EXTENSION);
$imagePath = __DIR__ . '/../' . $imageRelativePath;
$target_dir = dirname($imagePath);
if (!is_dir($target_dir)) {
mkdir($target_dir, 0755, true);
}
if (move_uploaded_file($tmpName, $imagePath)) {
$imagePaths[] = $imageRelativePath;
} else {
log_error('图片文件移动失败: ' . $images['name'][$key], __FILE__, __LINE__);
}
}
}
}
if (empty($error)) {
// 开始数据库事务
$conn->begin_transaction();
try {
// 确保必要变量存在,防止空值导致 SQL 错误
if (!isset($appName) || !isset($appDescription) || !isset($developerId) || !isset($version) || !isset($changelog) || !isset($ageRating) || !isset($ageRatingDescription)) {
throw new Exception('缺少必要的上传参数');
}
// 获取开发者邮箱
$userStmt = $conn->prepare('SELECT email FROM developers WHERE id = ?');
$userStmt->bind_param('i', $developerId);
$userStmt->execute();
$userResult = $userStmt->get_result();
$user = $userResult->fetch_assoc();
$developerEmail = $user['email'] ?? '';
$userStmt->close();
if (empty($developerEmail)) {
throw new Exception('无法获取开发者邮箱信息');
}
// 插入应用基本信息
$stmt = $conn->prepare('INSERT INTO apps (name, description, platforms, status, age_rating, age_rating_description, version, changelog, file_path, developer_id, developer_email, created_at) VALUES (?, ?, ?, \'pending\', ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)');
if (!$stmt) {
throw new Exception('应用基本信息查询准备失败: ' . $conn->error);
}
// 确保平台数据正确编码
$platforms = $_POST['platforms'] ?? [];
$platforms_json = json_encode($platforms);
// 此处需确认预处理语句占位符数量,确保与 bind_param 参数数量一致,示例仅示意,实际需根据表结构调整
// 修正参数绑定添加file_path参数以匹配SQL占位符数量
// 修正参数类型字符串长度确保与10个参数匹配
// 修正类型字符串长度10个参数对应10个类型字符
// 最终修正10个参数对应10个类型字符
// 根据参数实际类型修正类型字符串整数用i字符串用s
// 移除多余的$status参数匹配SQL中9个占位符
// 修正age_rating_description类型为字符串并确保9个参数与占位符匹配
// 修复变量名错误:使用已验证的$appFilePath替换未定义的$file_path
$stmt->bind_param('ssssssssis', $appName, $appDescription, $platforms_json, $ageRating, $ageRatingDescription, $version, $changelog, $appRelativePath, $developerId, $developerEmail);
if (!$stmt->execute()) {
throw new Exception('应用基本信息查询执行失败: ' . $stmt->error);
}
$appId = $stmt->insert_id;
log_info("应用已插入数据库: ID=$appId, 状态=pending", __FILE__, __LINE__);
$stmt->close();
// 发送审核邮件给管理员
require_once '../vendor/autoload.php';
$subject = '新应用待审核';
$body = "开发者提交了新应用应用ID: $appId名称: $appName请及时审核。";
$mail = new PHPMailer\PHPMailer\PHPMailer();
$mail->isSMTP();
$mail->Host = SMTP_HOST;
$mail->SMTPAuth = true;
$mail->Username = SMTP_USERNAME;
$mail->Password = SMTP_PASSWORD;
$mail->SMTPSecure = SMTP_ENCRYPTION;
$mail->Port = SMTP_PORT;
$mail->setFrom(SMTP_FROM_EMAIL, SMTP_FROM_NAME);
$mail->addAddress(ADMIN_EMAIL);
$mail->isHTML(false);
$mail->CharSet = 'utf-8';
$mail->Subject = $subject;
$mail->Body = $body;
if(!$mail->send()) {
log_error('发送审核邮件失败: ' . $mail->ErrorInfo, __FILE__, __LINE__);
} else {
log_info('已成功发送审核邮件给管理员', __FILE__, __LINE__);
}
log_info("开始处理应用关联数据: ID=$appId", __FILE__, __LINE__);
// 插入应用标签关联
foreach ($tags as $tagId) {
$tagStmt = $conn->prepare('INSERT INTO app_tags (app_id, tag_id) VALUES (?, ?)');
if (!$tagStmt) {
throw new Exception('标签关联查询准备失败: ' . $conn->error);
}
$tagStmt->bind_param('ii', $appId, $tagId);
if (!$tagStmt->execute()) {
throw new Exception('标签关联查询执行失败: ' . $tagStmt->error);
}
$tagStmt->close();
}
// 插入应用图片
foreach ($imagePaths as $imageRelativePath) {
$imageStmt = $conn->prepare('INSERT INTO app_images (app_id, image_path) VALUES (?, ?)');
if (!$imageStmt) {
throw new Exception('图片关联查询准备失败: ' . $conn->error);
}
$imageStmt->bind_param('is', $appId, $imageRelativePath);
if (!$imageStmt->execute()) {
throw new Exception('图片关联查询执行失败: ' . $imageStmt->error);
}
$imageStmt->close();
}
// 插入应用版本信息
$versionStmt = $conn->prepare('INSERT INTO app_versions (app_id, version, changelog, file_path, created_at) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)');
if (!$versionStmt) {
throw new Exception('版本信息查询准备失败: ' . $conn->error);
}
$versionStmt->bind_param('isss', $appId, $version, $changelog, $appRelativePath);
if (!$versionStmt->execute()) {
throw new Exception('版本信息查询执行失败: ' . $versionStmt->error);
}
$versionStmt->close();
log_info("所有关联数据处理完成,准备提交事务: ID=$appId", __FILE__, __LINE__);
// 提交事务
$conn->commit();
log_info("应用上传成功: ID=$appId, 状态=pending", __FILE__, __LINE__);
$success = '应用上传成功,请等待管理员审核';
} catch (Exception $e) {
// 回滚事务
$conn->rollback();
log_error('应用上传事务失败(ID=$appId): ' . $e->getMessage(), __FILE__, __LINE__);
$error = '上传应用时发生错误,请稍后再试';
}
}
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传应用 - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<script src="/js/sweetalert.js"></script>
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
.form-group {
margin-bottom: 1rem;
}
.btn-primary {
background-color: #007BFF;
border-color: #007BFF;
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
}
.back-link {
text-align: center;
margin-top: 1rem;
}
#ageRatingDescriptionGroup {
display: none;
}
</style>
<!-- Bootstrap JS with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
</head>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="dashboard.php">应用仪表盘</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="upload_app.php">上传应用</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<style>
.form-group {
margin-bottom: 1rem;
}
.btn-primary {
background-color: #007BFF;
border-color: #007BFF;
}
.btn-primary:hover {
background-color: #0056b3;
border-color: #0056b3;
}
.back-link {
text-align: center;
margin-top: 1rem;
}
#ageRatingDescriptionGroup {
display: none;
}
</style>
<script>
// 年龄分级说明显示控制
document.addEventListener('DOMContentLoaded', function() {
const ageRating = document.getElementById('age_rating');
const ageDescGroup = document.getElementById('ageRatingDescriptionGroup');
const ageDescInput = document.getElementById('age_rating_description');
function toggleAgeDescription() {
if (['12+', '17+'].includes(ageRating.value)) {
ageDescGroup.style.display = 'block';
ageDescInput.required = true;
} else {
ageDescGroup.style.display = 'none';
ageDescInput.required = false;
}
}
ageRating.addEventListener('change', toggleAgeDescription);
toggleAgeDescription(); // 初始状态检查
// 文件类型验证
const appFileInput = document.getElementById('app_file');
const imageInput = document.getElementById('images');
const allowedAppTypes = { 'android': ['apk'], 'ios': ['ipa'] };
const allowedImageTypes = ['jpg', 'jpeg', 'png', 'gif'];
appFileInput.addEventListener('change', function(e) {
if (this.files.length > 0) {
const file = this.files[0];
const ext = file.name.split('.').pop().toLowerCase();
if (file.size > 500 * 1024 * 1024) { // 100MB限制
Swal.fire({
title: '提示',
text: '文件大小不能超过500MB',
icon: 'warning',
confirmButtonText: '确定'
});
this.value = '';
}
}
});
imageInput.addEventListener('change', function(e) {
if (this.files.length > 0) {
for (let i = 0; i < this.files.length; i++) {
const file = this.files[i];
if (file.size > 10 * 1024 * 1024) { // 10MB限制
Swal.fire({
title: '提示',
text: `图片 ${file.name} 大小不能超过10MB`,
icon: 'warning',
confirmButtonText: '确定'
});
this.value = '';
return;
}
}
}
});
// 平台子选项显示控制
document.getElementById('windows').addEventListener('change', function() {
const suboptions = document.getElementById('windows_suboptions');
suboptions.style.display = this.checked ? 'block' : 'none';
if (!this.checked) {
document.querySelectorAll('input[name="windows_version"]').forEach(radio => radio.checked = false);
}
});
document.getElementById('linux').addEventListener('change', function() {
const suboptions = document.getElementById('linux_suboptions');
suboptions.style.display = this.checked ? 'block' : 'none';
if (!this.checked) {
document.querySelectorAll('input[name="linux_distribution"]').forEach(radio => radio.checked = false);
}
});
});
</script>
</head>
<body>
<div class="container mt-5 mb-5 col-md-8 col-lg-6">
<div class="card shadow-sm">
<div class="card-header bg-primary text-white">
<h2 class="h4 mb-0">上传应用</h2>
</div>
<div class="card-body p-4">
<?php if (!empty($error)): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<?php echo $error; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if (!empty($success)): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?php echo $success; ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<form method="post" enctype="multipart/form-data">
<div class="alert alert-warning mb-3">
<strong>警告:</strong>如果该应用非您本人开发,请务必添加"转载"标签。
</div>
<div class="form-group mb-3">
<label for="name" class="form-label">应用名称</label>
<input type="text" id="name" name="name" class="form-control" required>
</div>
<div class="form-group mb-3">
<label for="tags" class="form-label">标签</label>
<select id="tags" name="tags[]" multiple class="form-select" size="3">
<?php
$tagResult = $conn->query("SELECT id, name FROM tags");
while ($tag = $tagResult->fetch_assoc()):
?>
<option value="<?php echo $tag['id']; ?>"><?php echo htmlspecialchars($tag['name']); ?></option>
<?php endwhile; ?>
</select>
<small>按住Ctrl键可选择多个标签</small>
</div>
<div class="form-group mb-3">
<label for="description" class="form-label">应用描述支持Markdown</label>
<textarea id="description" name="description" rows="5" class="form-control" required></textarea>
</div>
<div class="form-group mb-3">
<label for="age_rating" class="form-label">年龄分级</label>
<select class="form-select" id="age_rating" name="age_rating" required>
<option value="3+">3+</option>
<option value="7+">7+</option>
<option value="12+">12+</option>
<option value="17+">17+</option>
</select>
</div>
<div class="form-group mb-3" id="ageRatingDescriptionGroup">
<label for="age_rating_description" class="form-label">年龄分级说明</label>
<textarea class="form-control" id="age_rating_description" name="age_rating_description" rows="3" placeholder="请说明为何需要此年龄分级"></textarea>
<small>当年龄分级为12+或以上时,此项为必填</small>
</div>
<div class="alert alert-info mb-3">
<strong>信息:</strong>我们尝试了开发多选子平台的功能但是就会导致以前的app会全部显示“未知平台”如果你是Windows的话选择Win7以上选项如果你是Linux的话选择APP支持平台中的任意一个即可。
</div>
<div class="form-group mb-3">
<label class="form-label">适用平台</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="android" id="android" name="platforms[]">
<label class="form-check-label" for="android">Android</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="ios" id="ios" name="platforms[]">
<label class="form-check-label" for="ios">iOS</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="windows" id="windows" name="platforms[]">
<label class="form-check-label" for="windows">Windows</label>
</div>
<div id="windows_suboptions" class="ms-4 mt-2" style="display: none;">
<div class="form-check">
<input class="form-check-input" type="radio" name="windows_version" id="windows_xp" value="windows_xp">
<label class="form-check-label" for="windows_xp">XP以前</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="windows_version" id="windows_win7" value="windows_win7">
<label class="form-check-label" for="windows_win7">Win7以后</label>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="macos" id="macos" name="platforms[]">
<label class="form-check-label" for="macos">macOS</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="linux" id="linux" name="platforms[]">
<label class="form-check-label" for="linux">Linux</label>
</div>
<div id="linux_suboptions" class="ms-4 mt-2" style="display: none;">
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_ubuntu" value="linux_ubuntu">
<label class="form-check-label" for="linux_ubuntu">Ubuntu</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_arch" value="linux_arch">
<label class="form-check-label" for="linux_arch">Arch Linux</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_centos" value="linux_centos">
<label class="form-check-label" for="linux_centos">CentOS</label>
</div>
</div>
</div>
<div class="form-group mb-3">
<label for="app_file" class="form-label">应用文件</label>
<input type="file" id="app_file" name="app_file" class="form-control" required>
</div>
<div class="form-group mb-3">
<label for="version" class="form-label">版本号</label>
<input type="text" id="version" name="version" class="form-control" required placeholder="例如: 1.0.0">
</div>
<div class="form-group mb-3">
<label for="changelog" class="form-label">更新日志</label>
<textarea id="changelog" name="changelog" rows="3" class="form-control" required></textarea>
</div>
<div class="form-group mb-4">
<label for="images" class="form-label">预览图片</label>
<input type="file" id="images" name="images[]" multiple accept="image/*" class="form-control">
<small>可选择多张图片</small>
</div>
<div class="form-group mb-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="age_doc_confirm" name="age_doc_confirm" required>
<label class="form-check-label" for="age_doc_confirm">
我已阅读并理解<a href="../docs/age_rating.php" target="_blank">年龄说明文档</a>
</label>
</div>
</div>
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-upload me-1"></i>上传应用</button>
</form>
<div class="back-link mt-4">
<a href="dashboard.php" class="btn btn-outline-secondary w-100">返回仪表盘</a>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,93 @@
<?php
// 引入配置文件
require_once '../config.php';
session_start();
$error = '';
$success = '';
// 检查是否提供了验证令牌
if (!isset($_GET['token']) || empty($_GET['token'])) {
$error = '无效的验证链接';
} else {
$token = trim($_GET['token']);
// 验证数据库连接
if (!($conn instanceof mysqli)) {
log_error('数据库连接错误: 连接不是MySQLi实例', __FILE__, __LINE__);
$error = '数据库连接错误,请稍后再试';
} else {
try {
// 查询具有该令牌的未验证开发者
$stmt = $conn->prepare('SELECT id FROM developers WHERE verification_token = ? AND is_verified = FALSE');
$stmt->bind_param('s', $token);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows === 1) {
// 更新验证状态
$updateStmt = $conn->prepare('UPDATE developers SET is_verified = TRUE, verified_at = NOW(), verification_token = NULL WHERE verification_token = ?');
$updateStmt->bind_param('s', $token);
$updateStmt->execute();
$updateStmt->close();
$success = '邮箱验证成功!现在您可以登录并创建应用了。';
} else {
$error = '验证链接无效或已过期';
}
$stmt->close();
} catch (Exception $e) {
log_error('邮箱验证失败: ' . $e->getMessage(), __FILE__, __LINE__);
$error = '验证过程中发生错误,请稍后再试';
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>邮箱验证 - <?= APP_STORE_NAME ?></title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<style>
body { background-color: #f4f4f4; padding: 70px 0; }
.container { max-width: 500px; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
.alert { margin-bottom: 20px; }
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?= APP_STORE_NAME ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="../index.php">首页</a></li>
<li class="nav-item"><a class="nav-link" href="login.php">开发者登录</a></li>
</ul>
</div>
</div>
</nav>
<div class="container">
<h2 class="mb-4">邮箱验证</h2>
<?php if (!empty($success)): ?>
<div class="alert alert-success" role="alert"><?= $success ?></div>
<a href="login.php" class="btn btn-primary"><i class="fas fa-sign-in-alt me-1"></i>前往登录</a>
<?php else: ?>
<div class="alert alert-danger" role="alert"><?= $error ?></div>
<a href="register.php" class="btn btn-secondary"><i class="fas fa-user-plus me-1"></i>重新注册</a>
<?php endif; ?>
</div>
<script src="/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,421 @@
<?php
require_once '../config.php';
require_once '../includes/logger.php';
session_start();
// 检查开发者登录状态
if (!isset($_SESSION['developer_id'])) {
header('Location: login.php');
exit;
}
$developerId = $_SESSION['developer_id'];
// 验证App ID
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header('Location: dashboard.php?error=无效的App ID');
exit;
}
$appId = $_GET['id'];
// 获取App信息并验证所有权
$app = null;
$getAppSql = "SELECT * FROM apps WHERE id = ? AND developer_id = ?";
$stmt = $conn->prepare($getAppSql);
if (!$stmt) {
log_error("应用所有权验证查询准备失败: " . $conn->error, __FILE__, __LINE__);
header('Location: dashboard.php?error=验证应用所有权失败');
exit;
}
$stmt->bind_param("ii", $appId, $developerId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
header('Location: dashboard.php?error=App不存在或无权访问');
exit;
}
$app = $result->fetch_assoc();
$platforms = json_decode($app['platforms'], true);
$success = '';
$error = '';
// 处理版本上传请求
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['upload_version'])) {
// 验证版本信息
if (empty($_POST['version']) || empty($_FILES['app_file']['name'])) {
$error = '版本号和安装包不能为空';
} else {
// 处理App文件上传
$uploadDir = '../files/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$fileName = basename($_FILES['app_file']['name']);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
$version = $_POST['version'];
$changelog = $_POST['changelog'] ?? '';
// 插入新版本记录
$insertVersionSql = "INSERT INTO app_versions (app_id, version, changelog, file_path) VALUES (?, ?, ?, ?)";
$verStmt = $conn->prepare($insertVersionSql);
if (!$verStmt) {
log_error("版本插入准备失败: " . $conn->error, __FILE__, __LINE__);
$error = '版本保存失败,请稍后再试';
unlink($targetPath); // 清理已上传文件
} else {
$verStmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
if ($verStmt->execute()) {
// 更新应用表中的最新版本
// 更新应用表中的最新版本
$updateAppSql = "UPDATE apps SET version = ? WHERE id = ?";
$updStmt = $conn->prepare($updateAppSql);
if (!$updStmt) {
log_error("应用版本更新准备失败: " . $conn->error, __FILE__, __LINE__);
$error = '更新应用版本失败,请稍后再试';
unlink($targetPath); // 数据库更新失败,删除文件
} else {
$updStmt->bind_param("si", $version, $appId);
$updStmt->execute();
$success = '版本上传成功';
}
} else {
$error = '版本保存失败: '. $conn->error;
unlink($targetPath); // 数据库更新失败,删除文件
}
}
} else {
$error = '文件上传失败';
}
}
}
// 处理版本修改请求
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['version_id'])) {
$versionId = $_POST['version_id'];
$version = $_POST['version'];
$changelog = $_POST['changelog'] ?? '';
// 检查是否有新文件上传
if (!empty($_FILES['new_app_file']['name'])) {
$uploadDir = '../files/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$fileName = basename($_FILES['new_app_file']['name']);
$newFilePath = $uploadDir . $fileName;
if (move_uploaded_file($_FILES['new_app_file']['tmp_name'], $newFilePath)) {
// 获取旧文件路径并删除
$getOldPathSql = "SELECT file_path FROM app_versions WHERE id = ?";
$getOldPathStmt = $conn->prepare($getOldPathSql);
if (!$getOldPathStmt) {
log_error("获取旧文件路径查询准备失败: " . $conn->error, __FILE__, __LINE__);
$error = '版本修改失败,请稍后再试';
unlink($newFilePath);
} else {
$getOldPathStmt->bind_param("i", $versionId);
$getOldPathStmt->execute();
$oldPathResult = $getOldPathStmt->get_result();
if ($oldPathResult->num_rows > 0) {
$oldPathRow = $oldPathResult->fetch_assoc();
$oldFilePath = $oldPathRow['file_path'];
if (file_exists($oldFilePath)) {
unlink($oldFilePath);
}
}
// 更新版本信息
$updateVersionSql = "UPDATE app_versions SET version = ?, changelog = ?, file_path = ? WHERE id = ?";
$updateVersionStmt = $conn->prepare($updateVersionSql);
if (!$updateVersionStmt) {
log_error("版本更新查询准备失败: " . $conn->error, __FILE__, __LINE__);
$error = '版本修改失败,请稍后再试';
unlink($newFilePath);
} else {
$updateVersionStmt->bind_param("sssi", $version, $changelog, $newFilePath, $versionId);
if ($updateVersionStmt->execute()) {
$success = '版本修改成功';
} else {
$error = '版本修改失败: ' . $conn->error;
unlink($newFilePath);
}
}
}
} else {
$error = '文件上传失败';
}
} else {
// 仅更新版本号和更新日志
$updateVersionSql = "UPDATE app_versions SET version = ?, changelog = ? WHERE id = ?";
$updateVersionStmt = $conn->prepare($updateVersionSql);
if (!$updateVersionStmt) {
log_error("版本更新查询准备失败: " . $conn->error, __FILE__, __LINE__);
$error = '版本修改失败,请稍后再试';
} else {
$updateVersionStmt->bind_param("ssi", $version, $changelog, $versionId);
if ($updateVersionStmt->execute()) {
$success = '版本修改成功';
} else {
$error = '版本修改失败: ' . $conn->error;
}
}
}
}
// 处理版本删除请求
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_version'])) {
$versionId = $_POST['version_id'];
$filePath = $_POST['file_path'];
// 删除文件
if (file_exists($filePath)) {
if (!unlink($filePath)) {
log_error("文件删除失败: " . $filePath, __FILE__, __LINE__);
$error = '版本删除失败,请稍后再试';
}
}
// 从数据库删除版本记录
$deleteVersionSql = "DELETE FROM app_versions WHERE id = ?";
$deleteVersionStmt = $conn->prepare($deleteVersionSql);
if (!$deleteVersionStmt) {
log_error("版本删除查询准备失败: " . $conn->error, __FILE__, __LINE__);
$error = '版本删除失败,请稍后再试';
} else {
$deleteVersionStmt->bind_param("i", $versionId);
if ($deleteVersionStmt->execute()) {
$success = '版本删除成功';
} else {
$error = '版本删除失败: ' . $conn->error;
}
}
if (!empty($success)) {
echo $success;
} else {
echo $error;
}
exit;
}
// 获取现有版本列表
$versions = [];
$getVersionsSql = "SELECT * FROM app_versions WHERE app_id = ? ORDER BY id DESC";
$verStmt = $conn->prepare($getVersionsSql);
if (!$verStmt) {
log_error("版本查询准备失败: " . $conn->error, __FILE__, __LINE__);
$error = '获取版本列表失败,请稍后再试';
} else {
$verStmt->bind_param("i", $appId);
$verStmt->execute();
$versionsResult = $verStmt->get_result();
while ($ver = $versionsResult->fetch_assoc()) {
$versions[] = $ver;
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>版本控制 - <?php echo htmlspecialchars($app['name']); ?></title>
<link href="../css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<link rel="stylesheet" href="../styles.css">
<script src="/js/sweetalert.js"></script>
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="dashboard.php">我的应用</a>
</li>
<li class="nav-item">
<a class="nav-link" href="upload_app.php">上传新应用</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="version_control.php?id=<?php echo $appId; ?>">版本控制</a>
</li>
<li class="nav-item">
<a class="nav-link" href="profile.php">个人资料</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<?php if (!empty($success)): ?>
<!-- <script>Swal.fire('成功', '<?php echo addslashes($success); ?>', 'success');</script> -->
<?php endif; ?>
<?php if (!empty($error)): ?>
<!-- <script>Swal.fire('错误', '<?php echo addslashes($error); ?>', 'error');</script> -->
<?php endif; ?>
<div class="card blur-bg mb-4">
<div class="card-header">
<h2>应用版本控制: <?php echo htmlspecialchars($app['name']); ?></h2>
</div>
<div class="card-body">
<h4>上传新版本</h4>
<form method="post" enctype="multipart/form-data" class="mb-4">
<div class="row g-3">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="version" name="version" placeholder="版本号" required>
<label for="version">版本号 (如: 1.0.0)</label>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="app_file" class="form-label">安装包文件</label>
<input class="form-control" type="file" id="app_file" name="app_file" required>
</div>
</div>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="changelog" name="changelog" rows="3" placeholder="更新日志"></textarea>
<label for="changelog">更新日志支持Markdown</label>
</div>
<button type="submit" class="btn btn-primary" name="upload_version"><i class="fas fa-cloud-upload-alt me-1"></i>上传新版本</button>
<a href="dashboard.php" class="btn btn-secondary ms-2"><i class="fas fa-arrow-left me-1"></i>返回</a>
</form>
<hr>
<h4>版本历史</h4>
<?php if (empty($versions)): ?>
<div class="alert alert-info">暂无版本记录</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>版本号</th>
<th>上传时间</th>
<th>更新日志</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($versions as $ver): ?>
<tr>
<td><?php echo htmlspecialchars($ver['version']); ?></td>
<td><?php echo htmlspecialchars($ver['upload_time']); ?></td>
<td><?php echo nl2br(htmlspecialchars($ver['changelog'] ?: '无')); ?></td>
<td>
<a href="../download.php?id=<?php echo $ver['id']; ?>&type=version" class="btn btn-sm btn-outline-primary"><i class="fas fa-download me-1"></i>下载</a>
<a href="#" class="btn btn-sm btn-outline-warning ms-2" onclick="openEditModal(<?php echo $ver['id']; ?>, '<?php echo htmlspecialchars($ver['version']); ?>', '<?php echo htmlspecialchars($ver['changelog']); ?>')"><i class="fas fa-edit me-1"></i>修改</a>
<a href="#" class="btn btn-sm btn-outline-danger ms-2" onclick="confirmDelete(<?php echo $ver['id']; ?>, '<?php echo htmlspecialchars($ver['file_path']); ?>')"><i class="fas fa-trash-alt me-1"></i>删除</a>
<?php if ($ver['is_current'] == 1): ?>
<span class="badge bg-success">当前版本</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script src="../js/bootstrap.bundle.js"></script>
<script>
function openEditModal(versionId, version, changelog) {
const modal = `
<div class="modal fade" id="editVersionModal" tabindex="-1" aria-labelledby="editVersionModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editVersionModalLabel">修改版本</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="editVersionForm" method="post" enctype="multipart/form-data">
<div class="modal-body">
<input type="hidden" name="version_id" value="${versionId}">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="editVersion" name="version" value="${version}" required>
<label for="editVersion">版本号</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="editChangelog" name="changelog" rows="3">${changelog}</textarea>
<label for="editChangelog">更新日志</label>
</div>
<div class="mb-3">
<label for="new_app_file" class="form-label">更新App文件 (可选)</label>
<input class="form-control" type="file" id="new_app_file" name="new_app_file">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><i class="fas fa-times me-1"></i>取消</button>
<button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>保存修改</button>
</div>
</form>
</div>
</div>
</div>`;
document.body.insertAdjacentHTML('beforeend', modal);
const editModal = new bootstrap.Modal(document.getElementById('editVersionModal'));
editModal.show();
document.getElementById('editVersionForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('version_control.php', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => {
editModal.hide();
window.location.reload();
})
.catch(error => {
editModal.hide();
window.location.reload();
});
});
}
function confirmDelete(versionId, filePath) {
const formData = new FormData();
formData.append('delete_version', 'true');
formData.append('version_id', versionId);
formData.append('file_path', filePath);
fetch('version_control.php', { method: 'POST', body: formData })
.then(response => response.text())
.then(data => {
window.location.reload();
})
.catch(error => {
window.location.reload();
});
}
</script>
</body>
</html>

245
developer_apps.php Normal file
View File

@@ -0,0 +1,245 @@
<?php
session_start();
require_once 'config.php';
if (!defined('APP_STORE_NAME')) {
define('APP_STORE_NAME', '');
}
// 验证开发者ID参数
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
header('Location: index.php');
exit;
}
$developerId = intval($_GET['id']);
// 获取开发者信息
$sqlDeveloper = "SELECT username, social_links FROM developers WHERE id = $developerId";
$resultDeveloper = $conn->query($sqlDeveloper);
$developer = $resultDeveloper->fetch_assoc();
// 确定页面标题和开发者名称
if ($developer) {
$developerName = htmlspecialchars($developer['username']);
$pageTitle = $developerName . ' 的应用 - ' . APP_STORE_NAME;
} else {
// 如果开发者ID为0或不存在视为管理员
$developerName = '管理员';
$pageTitle = '管理员的应用 - ' . APP_STORE_NAME;
}
// 获取该开发者的所有应用
$sqlApps = "SELECT a.*, (SELECT AVG(rating) FROM reviews WHERE app_id = a.id) as avg_rating
FROM apps a
WHERE a.developer_id = $developerId";
$resultApps = $conn->query($sqlApps);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $pageTitle; ?></title>
<!-- Bootstrap CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<!-- 自定义CSS -->
<link rel="stylesheet" href="styles.css">
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">首页</a>
</li>
<?php if (isset($_SESSION['admin'])): ?>
<li class="nav-item">
<a class="nav-link" href="/admin/">管理</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<form id="searchForm" class="mb-4">
<div class="row g-3">
<div class="col-md-10">
<div class="form-floating">
<input type="text" class="form-control" id="searchApp" placeholder="搜索应用" value="">
<label for="searchApp">搜索应用</label>
</div>
</div>
<div class="col-md-2">
<button class="btn btn-primary w-100" style="width: calc(3.5rem + calc(var(--bs-border-width) * 2)); height: calc(3.5rem + calc(var(--bs-border-width) * 2))" type="submit" id="searchButton">搜索</button>
</div>
</div>
</form>
<h1><?php echo $developerName; ?> 的应用</h1>
<hr>
<?php if (isset($developer['social_links']) && !empty($developer['social_links'])): ?>
<div class="mb-4">
<h5>开发者链接</h5>
<div class="d-flex flex-wrap gap-2">
<?php
$socialLinks = explode(',', $developer['social_links']);
foreach ($socialLinks as $link):
$link = trim($link);
if (!empty($link)):
?>
<a href="<?php echo htmlspecialchars($link); ?>" target="_blank" class="btn btn-outline-primary btn-sm">
<i class="fas fa-link me-1"></i>
<?php echo parse_url($link, PHP_URL_HOST) ?: $link; ?>
</a>
<?php
endif;
endforeach;
?>
</div>
</div>
<?php endif; ?>
<?php if ($resultApps && $resultApps->num_rows > 0): ?>
<div class="row">
<?php while ($app = $resultApps->fetch_assoc()): ?>
<div class="col-md-4 mb-4">
<div class="card h-100 blur-bg">
<?php
// 获取应用的第一张图片
$sqlImage = "SELECT image_path FROM app_images WHERE app_id = ". $app['id'] ." LIMIT 1";
$resultImage = $conn->query($sqlImage);
$image = $resultImage ? $resultImage->fetch_assoc() : null;
$imagePath = $image ? $image['image_path'] : 'default-app.png';
?>
<div class="card-body">
<h5 class="card-title"><?php echo htmlspecialchars($app['name']); ?></h5>
<p class="card-text"><?php echo htmlspecialchars(substr($app['description'], 0, 100)); ?>...</p>
<?php
// 获取应用标签
$tagSql = "SELECT t.name FROM tags t JOIN app_tags at ON t.id = at.tag_id WHERE at.app_id = ?";
$tagStmt = $conn->prepare($tagSql);
$tagStmt->bind_param('i', $app['id']);
$tagStmt->execute();
$tagResult = $tagStmt->get_result();
$tags = [];
while ($tag = $tagResult->fetch_assoc()) {
$tags[] = htmlspecialchars($tag['name']);
}
$tagStmt->close();
// 获取应用适用平台
$platforms = json_decode($app['platforms'], true);
echo '<p class="card-text">标签: '. implode(', ', $tags) . '</p>';
echo '<p class="card-text">平台: ';
foreach ($platforms as $platform) {
$icon = '';
if ($platform === 'Windows') {
$icon = '<i class="fab fa-windows"></i>';
} elseif ($platform === 'macOS') {
$icon = '<i class="fab fa-apple"></i>';
} elseif ($platform === 'Linux') {
$icon = '<i class="fab fa-linux"></i>';
} elseif ($platform === 'iOS') {
$icon = '<i class="fab fa-app-store-ios"></i>';
} elseif ($platform === 'Android') {
$icon = '<i class="fab fa-android"></i>';
}
echo " <span class='d-inline-flex align-items-center'>$icon $platform</span>";
}
echo '</p>';
?>
<p class="card-text">
<small class="text-muted">
评分: <?php echo round($app['avg_rating'] ?? 0, 1); ?>/5
</small>
</p>
<a href="app.php?id=<?php echo $app['id']; ?>" class="btn btn-primary">查看详情</a>
</div>
</div>
</div>
<?php endwhile; ?>
</div>
<?php else: ?>
<div class="alert alert-info" role="alert">
<?php echo $developerName; ?> 暂无上传应用
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
// 搜索功能实现
function performSearch(event) {
event.preventDefault();
const searchTerm = document.getElementById('searchApp').value.toLowerCase();
const appCards = document.querySelectorAll('.col-md-4.mb-4');
let hasResults = false;
appCards.forEach(card => {
const title = card.querySelector('.card-title').textContent.toLowerCase();
const description = card.querySelector('.card-text').textContent.toLowerCase();
if (title.includes(searchTerm) || description.includes(searchTerm)) {
card.style.display = '';
hasResults = true;
} else {
card.style.display = 'none';
}
});
// 如果没有搜索结果,显示提示
if (!hasResults) {
Swal.fire({
title: '未找到结果',
text: '没有找到与 "' + searchTerm + '" 匹配的应用',
icon: 'info',
confirmButtonText: '确定'
});
}
}
// 添加表单提交事件监听
document.getElementById('searchForm').addEventListener('submit', performSearch);
});
</script>
</body>
</html>

68
docs/age_rating.php Normal file
View File

@@ -0,0 +1,68 @@
<?php require_once '../config.php'; ?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LeonAPP年龄分级</title>
<link rel="stylesheet" href="../css/bootstrap.min.css">
<link rel="stylesheet" href="/styles.css">
<script src="/js/marked.js"></script>
<style>
.markdown-container {
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
}
.markdown-container h1 {
margin-bottom: 2rem;
color: #333;
}
</style>
<style>
body {
padding-top: 56px;
}
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg fixed-top">
<div class="container">
<a href="/index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="/index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/index.php">首页</a>
</li>
<?php if (isset($_SESSION['admin'])): ?>
<li class="nav-item">
<a class="nav-link" href="/admin/">管理</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
<div class="container markdown-container">
<div id="markdown-content"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 读取Markdown文件内容并渲染
const markdownContent = <?php echo json_encode(file_get_contents('markdown/la-1.md')); ?>;
const htmlContent = marked.parse(markdownContent);
document.getElementById('markdown-content').innerHTML = htmlContent;
});
</script>
</body>
</html>

View File

@@ -0,0 +1,259 @@
<?php
/**
* APP 审核标准文档 - 完整版
*/
?>
<?php require_once '../config.php'; ?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APP 审核标准 - 完整版</title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<link rel="stylesheet" href="/styles.css">
<style>
body {
padding-top: 56px;
}
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
</style>
<style>
.audit-section {
margin-bottom: 2rem;
padding: 1.5rem;
border-radius: 0.5rem;
background-color: #f8f9fa;
}
.audit-subsection {
margin-bottom: 1.5rem;
padding-left: 1rem;
border-left: 3px solid #0d6efd;
}
.audit-point {
margin-bottom: 0.75rem;
}
.audit-note {
color: #6c757d;
font-size: 0.9rem;
margin-top: 0.25rem;
margin-left: 1.5rem;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg fixed-top">
<div class="container">
<a href="/index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="/index.php"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="/index.php">首页</a>
</li>
<?php if (isset($_SESSION['admin'])): ?>
<li class="nav-item">
<a class="nav-link" href="/admin/">管理</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
<div class="container mt-5 mb-5">
<div class="text-center mb-5">
<h1 class="display-4">APP 审核标准文档</h1>
<p class="lead">适用于所有平台的应用审核规范</p>
<p class="text-muted">版本2025.07 修订</p>
</div>
<div class="audit-section">
<h2 class="fw-bold mb-4">一、内容审核标准</h2>
<div class="audit-subsection">
<h3 class="fw-semibold">1.1 法律法规合规性</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">1.1.1</span> 不得包含任何违反中国法律法规的内容,如赌博、色情、暴力、恐怖主义等相关信息
<p class="audit-note">包括但不限于文字、图片、音频、视频等形式的违法内容</p>
</li>
<li class="audit-point">
<span class="fw-medium">1.1.2</span> 不得含有侵犯他人隐私、名誉权、肖像权等个人权利的内容
<p class="audit-note">需确保用户数据收集、使用和共享符合《个人信息保护法》</p>
</li>
<li class="audit-point">
<span class="fw-medium">1.1.3</span> 不得传播谣言、虚假信息或误导性内容
<p class="audit-note">特别是涉及金融、医疗、时政等敏感领域的信息</p>
</li>
</ul>
</div>
<div class="audit-subsection">
<h3 class="fw-semibold">1.2 知识产权保护</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">1.2.1</span> 不得包含侵犯他人商标、专利、著作权等知识产权的内容
<p class="audit-note">应用内所有内容需确保有合法授权或属于原创</p>
</li>
<li class="audit-point">
<span class="fw-medium">1.2.2</span> 不得抄袭或模仿已有应用的界面设计、功能逻辑或商业模式
<p class="audit-note">需具备显著的原创性和独特价值</p>
</li>
</ul>
</div>
<div class="audit-subsection">
<h3 class="fw-semibold">1.3 广告与推广内容</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">1.3.1</span> 所有广告内容必须真实、合法,不得含有虚假或引人误解的宣传
<p class="audit-note">广告需明确标识,与应用内容有清晰区分</p>
</li>
<li class="audit-point">
<span class="fw-medium">1.3.2</span> 不得诱导用户点击广告或进行不必要的操作
<p class="audit-note">禁止使用欺骗性标题、虚假下载按钮等手段</p>
</li>
</ul>
</div>
</div>
<div class="audit-section">
<h2 class="fw-bold mb-4">二、功能审核标准</h2>
<div class="audit-subsection">
<h3 class="fw-semibold">2.1 功能完整性</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">2.1.1</span> 应用必须实现所有宣称的核心功能,无缺失或未完成的功能模块
<p class="audit-note">需与应用商店描述、宣传材料一致</p>
</li>
<li class="audit-point">
<span class="fw-medium">2.1.2</span> 所有功能必须可正常使用,无崩溃、闪退或无法访问的情况
<p class="audit-note">包括但不限于注册登录、数据提交、支付流程等</p>
</li>
</ul>
</div>
<div class="audit-subsection">
<h3 class="fw-semibold">2.2 用户界面与交互</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">2.2.1</span> 界面设计需符合目标平台的设计规范如iOS Human Interface Guidelines或Android Material Design
<p class="audit-note">包括布局、色彩、图标、字体等视觉元素的一致性</p>
</li>
<li class="audit-point">
<span class="fw-medium">2.2.2</span> 交互逻辑清晰,操作流程合理,无歧义或容易引起用户误解的设计
<p class="audit-note">例如按钮响应区域足够大,导航逻辑明确</p>
</li>
<li class="audit-point">
<span class="fw-medium">2.2.3</span> 支持多种屏幕尺寸和分辨率,在不同设备上显示正常
<p class="audit-note">需进行全面的兼容性测试</p>
</li>
</ul>
</div>
<div class="audit-subsection">
<h3 class="fw-semibold">2.3 数据安全与隐私</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">2.3.1</span> 应用必须有明确的隐私政策,说明数据收集、使用和共享方式
<p class="audit-note">隐私政策需符合相关法律法规要求</p>
</li>
<li class="audit-point">
<span class="fw-medium">2.3.2</span> 敏感数据(如密码、支付信息等)必须进行加密处理
<p class="audit-note">推荐使用HTTPS协议进行数据传输</p>
</li>
<li class="audit-point">
<span class="fw-medium">2.3.3</span> 不得在用户未明确授权的情况下收集、使用或共享个人数据
<p class="audit-note">特别是位置、联系人、摄像头等敏感权限</p>
</li>
</ul>
</div>
</div>
<div class="audit-section">
<h2 class="fw-bold mb-4">三、性能审核标准</h2>
<div class="audit-subsection">
<h3 class="fw-semibold">3.1 响应速度与稳定性</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">3.1.1</span> 应用启动时间不得超过3秒冷启动和1秒热启动
<p class="audit-note">对于复杂应用,启动时间可适当放宽,但需提供合理说明</p>
</li>
<li class="audit-point">
<span class="fw-medium">3.1.2</span> 界面切换和操作响应时间不得超过500毫秒
<p class="audit-note">需避免长时间无响应或卡顿现象</p>
</li>
<li class="audit-point">
<span class="fw-medium">3.1.3</span> 在正常使用场景下,应用不得出现崩溃、闪退或无响应的情况
<p class="audit-note">需进行至少5000次操作的稳定性测试</p>
</li>
</ul>
</div>
<div class="audit-subsection">
<h3 class="fw-semibold">3.2 资源占用</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">3.2.1</span> 应用在 idle 状态下内存占用不得超过100MB
<p class="audit-note">复杂应用(如图形处理、游戏等)可适当放宽</p>
</li>
<li class="audit-point">
<span class="fw-medium">3.2.2</span> 应用不得过度消耗设备CPU、电池等资源
<p class="audit-note">禁止在后台执行不必要的操作或频繁唤醒设备</p>
</li>
</ul>
</div>
<div class="audit-subsection">
<h3 class="fw-semibold">3.3 兼容性</h3>
<ul class="list-unstyled">
<li class="audit-point">
<span class="fw-medium">3.3.1</span> 应用必须支持目标平台的主流版本如iOS 14+Android 8.0+
<p class="audit-note">需在审核时提供支持版本列表</p>
</li>
<li class="audit-point">
<span class="fw-medium">3.3.2</span> 应用必须在主流设备型号上正常运行,无显示异常或功能缺失
<p class="audit-note">至少覆盖市场占有率前80%的设备</p>
</li>
</ul>
</div>
</div>
<div class="audit-section">
<h2 class="fw-bold mb-4">四、审核流程与结果</h2>
<div class="audit-subsection">
<h3 class="fw-semibold">4.1 审核周期</h3>
<p>标准审核周期为5个工作日紧急审核可在24小时内完成需额外支付加急费用支付到<a href="https://afdian.com/a/leonmmcoset">站长爱发电</a>使用自定义金额并备注5元</p>
</div>
<div class="audit-subsection">
<h3 class="fw-semibold">4.2 审核结果通知</h3>
<p>审核结果将通过邮件和平台内消息通知开发者,包含审核通过或拒绝上线两种结果</p>
</div>
<div class="audit-subsection">
<h3 class="fw-semibold">4.3 申诉机制</h3>
<p>如对审核结果有异议开发者可在收到通知后7个工作日内向<a href="mailto:leonmmcoset@outlook.com">站长邮件</a>提交申诉我们将在3个工作日内进行复核</p>
</div>
</div>
<div class="text-center mt-6">
<p class="text-muted">© 2025 LeonAPP. 保留所有权利.</p>
</div>
</div>
<script src="/js/bootstrap.bundle.js"></script>
</body>
</html>

12
docs/markdown/la-1.md Normal file
View File

@@ -0,0 +1,12 @@
# LeonAPP年龄分级文档
## 简介
LeonAPP年龄分级LeonAPP Age Classification简称LAC是由LeonMMcoset提出的年龄分级标准主要运用在LeonAPP应用商城。旨在帮助未成年人找到适合自己身心健康的应用。
## 各部分年龄分级
### **3+**
适合3岁及以上儿童使用。内容无任何暴力、恐怖、色情及不良引导元素以简单易懂的互动、基础认知内容如数字、颜色、简单故事等为主界面设计友好操作简单家长可放心让孩子独立使用有助于儿童启蒙认知与基础能力培养。
### **7+**
适合7岁及以上用户。内容可能包含少量轻度幻想元素或简单竞争场景如儿童游戏中的良性比赛但无不当言行或危险引导整体积极健康。部分内容需要一定理解能力能帮助用户在互动中提升逻辑思维、社交协作等基础能力家长可根据孩子情况进行适当引导。
### **12+**
适合12岁及以上用户。内容可能涉及较复杂的情节、中度竞争元素或少量不涉及血腥暴力的冲突场景语言表达上可能包含一些符合青少年认知的抽象概念。部分内容需要一定的判断力和自控力建议家长了解内容方向引导用户合理安排使用时间培养健康的使用习惯。
### **17+**
适合17岁及以上成年用户。内容可能包含成熟的主题如复杂的社会问题、情感纠葛、一定程度的暴力描写非血腥残酷、少量成人化语言或暗示性内容需要具备成熟的价值观和判断力才能理解。该分级内容不适合未成年人接触建议成年用户合理控制使用时长避免过度沉浸。

47
docs/privacy_policy.php Normal file
View File

@@ -0,0 +1,47 @@
<?php
session_start();
require_once '../config.php';
if (!isset($conn) || !$conn instanceof mysqli) {
die('数据库连接失败,请检查配置文件。');
}?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>隐私政策</title>
<style> .page-transition { animation: fadeIn 0.5s ease-in-out; } @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } </style> <!-- Bootstrap CSS --> <link href="../css/bootstrap.min.css" rel="stylesheet"> <!-- 自定义CSS -->
<link rel="stylesheet" href="../styles.css">
<!-- Fluent Design 模糊效果 -->
<!-- Bootstrap JS Bundle with Popper -->
<script src="../js/bootstrap.bundle.js"></script> <style> .blur-bg { backdrop-filter: blur(10px); background-color: rgba(255, 255, 255, 0.5); } </style></head><body class="page-transition"> <!-- 导航栏 --> <nav class="navbar navbar-expand-lg navbar-light blur-bg"> <div class="container"> <a href="../index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="#"><?php echo APP_STORE_NAME; ?></a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" href="../index.php">首页</a> </li> <?php if (isset($_SESSION['admin'])): ?> <li class="nav-item"> <a class="nav-link" href="../admin/">管理</a> </li> <?php endif; ?> <li class="nav-item"> <a class="nav-link" href="../tags.php">标签</a> </li> <?php if (isset($_SESSION['developer_id'])): ?> <li class="nav-item"> <a class="nav-link" href="../developer/dashboard.php">进入面板</a> </li> <?php else: ?> <li class="nav-item"> <a class="nav-link" href="../developer/register.php">开发者注册</a> </li> <li class="nav-item"> <a class="nav-link" href="../developer/login.php">开发者登录</a> </li> <?php endif; ?> </ul> </div> </div> </nav> <div class="container mt-4">
<h1>隐私政策</h1>
<h2>引言</h2>
<p>本隐私政策旨在说明我们如何收集、使用、披露和保护您的个人信息。在使用我们的服务前,请仔细阅读本隐私政策。</p>
<h2>信息收集</h2>
<p>我们会收集您在使用服务时主动提供的信息,例如注册信息等。同时,我们也会自动收集一些信息,如设备信息、日志信息等。</p>
<h2>信息使用</h2>
<p>我们会将收集到的信息用于提供、维护和改进服务,个性化用户体验,处理交易,以及遵守法律要求等。</p>
<h2>信息披露</h2>
<p>除非获得您的同意,或者根据法律要求,否则我们不会向第三方披露您的个人信息。在某些情况下,我们可能会与合作伙伴共享信息,但会确保他们遵守严格的数据保护要求。</p>
<h2>信息保护</h2>
<p>我们采用合理的安全措施来保护您的个人信息,防止信息被未经授权的访问、使用或披露。但请您理解,没有任何一种互联网传输方式或电子存储方式是 100% 安全的。</p>
<h2>您的权利</h2>
<p>您有权访问、更正、删除您的个人信息,以及限制我们对您信息的处理。如果您有任何相关请求,请联系我们。</p>
<h2>政策变更</h2>
<p>我们可能会定期更新本隐私政策。更新后,我们会在网站上发布新的隐私政策,并说明变更的生效日期。请您定期查看本政策,以了解我们的信息处理方式是否有变化。</p>
<h2>联系我们</h2>
<p>如果您对本隐私政策有任何疑问或建议,请通过 [您的联系方式] 与我们联系。</p>
</div>
</body>
</html>

54
download.php Normal file
View File

@@ -0,0 +1,54 @@
<?php
ob_start();
require_once 'config.php';
// 验证版本ID
if (!isset($_GET['version_id']) || !is_numeric($_GET['version_id'])) {
http_response_code(400);
exit('无效的版本ID');
}
$versionId = $_GET['version_id'];
// 获取版本信息
$version = null;
$getVersionSql = "SELECT * FROM app_versions WHERE id = ?";
$stmt = $conn->prepare($getVersionSql);
$stmt->bind_param("i", $versionId);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows !== 1) {
http_response_code(404);
exit('版本不存在');
}
$version = $result->fetch_assoc();
// 获取绝对文件路径
$filePath = realpath(__DIR__ . '/' . $version['file_path']);
// 验证文件存在性
if (!$filePath || !file_exists($filePath)) {
http_response_code(404);
exit('文件不存在');
}
// 设置下载响应头
$fileName = basename($filePath);
$fileSize = filesize($filePath);
// 清除输出缓冲区并发送 headers
ob_end_clean();
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($fileName));
header('Content-Length: ' . $fileSize);
header('Cache-Control: no-cache, must-revalidate');
header('Expires: 0');
header('Pragma: public');
// 输出文件内容
if (!readfile($filePath)) {
http_response_code(500);
exit('无法读取文件');
}
exit;
?>

61
error_pages/404.html Normal file
View File

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - 页面未找到</title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f8f9fa;
}
.error-container {
text-align: center;
padding: 2rem;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
font-size: 5rem;
margin: 0;
color: #6c757d;
}
p {
font-size: 1.2rem;
margin: 1rem 0;
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body class="page-transition">
<div class="error-container">
<h1>404</h1>
<h2>页面未找到</h2>
<p>您请求的页面不存在或已被移动。</p>
<a href="/" class="btn btn-primary mt-3">返回首页</a>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
</body>
</html>

62
error_pages/500.html Normal file
View File

@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>500 - 服务器内部错误</title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f8f9fa;
}
.error-container {
text-align: center;
padding: 2rem;
background-color: white;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
font-size: 5rem;
margin: 0;
color: #dc3545;
}
p {
font-size: 1.2rem;
margin: 1rem 0;
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body class="page-transition">
<div class="error-container">
<h1>500</h1>
<h2>服务器内部错误</h2>
<p>服务器遇到意外错误,无法完成您的请求。</p>
<p>我们的技术团队已收到通知,正在处理此问题。</p>
<a href="/" class="btn btn-primary mt-3">返回首页</a>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
</body>
</html>

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
favicon.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

2166
favicon.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 163 KiB

160
history_announcements.php Normal file
View File

@@ -0,0 +1,160 @@
<?php
// 包含配置文件
require_once 'config.php';
// 使用config.php中的数据库连接
if (!$conn) {
echo '<script>swal("错误", "数据库连接失败", "error");</script>';
exit;
}
// 查询所有公告,按发布时间倒序
$sql = "SELECT id, title, content, created_at FROM announcements ORDER BY created_at DESC";
$result = $conn->query($sql);
?>
<?php
session_start();
require_once 'config.php';
if (!isset($conn) || !$conn instanceof mysqli) {
die('数据库连接失败,请检查配置文件。');
}?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>历史公告 - <?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- 自定义CSS -->
<link rel="stylesheet" href="styles.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<script src="/js/sweetalert.js"></script>
<!-- Marked.js 用于Markdown解析 -->
<script src="js/marked.js"></script>
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<style>
.announcement-item {
margin-bottom: 2rem;
padding: 1rem;
border: 1px solid #e9ecef;
border-radius: 0.3rem;
}
.announcement-item h3 {
margin-bottom: 0.5rem;
color: #343a40;
}
.date {
color: #6c757d;
font-size: 0.9rem;
margin-bottom: 1rem;
}
.content {
color: #495057;
line-height: 1.6;
}
</style>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="#"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">首页</a>
</li>
<?php if (isset($_SESSION['admin'])): ?>
<li class="nav-item">
<a class="nav-link" href="/admin/">管理</a>
</li>
<?php endif; ?>
<li class="nav-item">
<a class="nav-link" href="tags.php">标签</a>
</li>
<?php if (isset($_SESSION['developer_id'])): ?>
<li class="nav-item">
<a class="nav-link" href="developer/dashboard.php">进入面板</a>
</li>
<?php else: ?>
<li class="nav-item">
<a class="nav-link" href="developer/register.php">开发者注册</a>
</li>
<li class="nav-item">
<a class="nav-link" href="developer/login.php">开发者登录</a>
</li>
<?php endif; ?>
<li class="nav-item">
<a class="nav-link" href="http://leonmmcoset.jjxmm.win:8010/app.php?id=74">下载LeonAPP手机版</a>
</li>
<li class="nav-item">
<a class="nav-link" href="http://leonmmcoset.jjxmm.win:8010/app.php?id=41">下载LeonAPP电脑版</a>
</li>
<li class="nav-item">
<a class="nav-link" href="thanks.php">鸣谢</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-5">
<h1 class="mb-4">历史公告</h1>
<?php if ($result && $result->num_rows > 0): ?>
<div class="announcements-list">
<?php while($announcement = $result->fetch_assoc()): ?>
<div class="announcement-item">
<h3><?php echo htmlspecialchars($announcement['title'], ENT_QUOTES); ?></h3>
<p class="date"><?php echo htmlspecialchars($announcement['created_at'], ENT_QUOTES); ?></p>
<div class="content markdown-content"><?php echo htmlspecialchars($announcement['content'], ENT_QUOTES); ?></div>
</div>
<?php endwhile; ?>
</div>
<?php else: ?>
<div class="alert alert-info">
暂无历史公告记录。
</div>
<?php endif; ?>
</div>
<script src="js/bootstrap.bundle.js"></script>
<script>
// 解析所有公告的Markdown内容
document.addEventListener('DOMContentLoaded', function() {
const contentElements = document.querySelectorAll('.markdown-content');
contentElements.forEach(function(element) {
const markdown = element.textContent;
element.innerHTML = marked.parse(markdown);
});
});
</script>
</body>
</html>

56
includes/logger.php Normal file
View File

@@ -0,0 +1,56 @@
<?php
/**
* Logging utility for the application
* Provides log_info() and log_error() functions
*/
if (!function_exists('log_info')) {
/**
* Log informational message
* @param string $message The message to log
* @param string $file Optional file name where the log was called
* @param int $line Optional line number where the log was called
*/
function log_info($message, $file = '', $line = 0) {
$logFile = __DIR__ . '/../logs/app_' . date('Y-m-d') . '.log';
// Create logs directory if it doesn't exist
if (!file_exists(dirname($logFile))) {
mkdir(dirname($logFile), 0755, true);
}
$prefix = '[' . date('Y-m-d H:i:s') . '] [INFO]';
if (!empty($file) && $line > 0) {
$prefix .= ' [' . basename($file) . ':' . $line . ']';
}
$logMessage = $prefix . ' ' . $message . PHP_EOL;
file_put_contents($logFile, $logMessage, FILE_APPEND);
}
}
if (!function_exists('log_error')) {
/**
* Log error message
* @param string $message The error message to log
* @param string $file Optional file name where the error occurred
* @param int $line Optional line number where the error occurred
*/
function log_error($message, $file = '', $line = 0) {
$logFile = __DIR__ . '/../logs/error_' . date('Y-m-d') . '.log';
// Create logs directory if it doesn't exist
if (!file_exists(dirname($logFile))) {
mkdir(dirname($logFile), 0755, true);
}
$prefix = '[' . date('Y-m-d H:i:s') . '] [ERROR]';
if (!empty($file) && $line > 0) {
$prefix .= ' [' . basename($file) . ':' . $line . ']';
}
$logMessage = $prefix . ' ' . $message . PHP_EOL;
file_put_contents($logFile, $logMessage, FILE_APPEND);
}
}
?>

499
index.php Normal file
View File

@@ -0,0 +1,499 @@
<?php
session_start();
require_once 'config.php';
if (!isset($conn) || !$conn instanceof mysqli) {
die('数据库连接失败,请检查配置文件。');
}?>
<!DOCTYPE html>
<style>
.page-transition {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo APP_STORE_NAME; ?></title>
<!-- Bootstrap CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- 自定义CSS -->
<link rel="stylesheet" href="styles.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="/css/all.min.css">
<script src="/js/sweetalert.js"></script>
<!-- Marked.js 用于Markdown解析 -->
<script src="js/marked.js"></script>
<!-- Fluent Design 模糊效果 -->
<style>
.blur-bg {
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.5);
}
</style>
</head>
<body class="page-transition">
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
<div class="container">
<a href="index.php"><img src="/favicon.jpeg" alt="Logo" style="height: 30px; margin-right: 10px; border-radius: var(--border-radius);"></a>
<a class="navbar-brand" href="#"><?php echo APP_STORE_NAME; ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php">首页</a>
</li>
<?php if (isset($_SESSION['admin'])): ?>
<li class="nav-item">
<a class="nav-link" href="/admin/">管理</a>
</li>
<?php endif; ?>
<li class="nav-item">
<a class="nav-link" href="tags.php">标签</a>
</li>
<?php if (isset($_SESSION['developer_id'])): ?>
<li class="nav-item">
<a class="nav-link" href="developer/dashboard.php">进入面板</a>
</li>
<?php else: ?>
<li class="nav-item">
<a class="nav-link" href="developer/register.php">开发者注册</a>
</li>
<li class="nav-item">
<a class="nav-link" href="developer/login.php">开发者登录</a>
</li>
<?php endif; ?>
<li class="nav-item">
<a class="nav-link" href="http://leonmmcoset.jjxmm.win:8010/app.php?id=74">下载LeonAPP手机版</a>
</li>
<li class="nav-item">
<a class="nav-link" href="http://leonmmcoset.jjxmm.win:8010/app.php?id=41">下载LeonAPP电脑版</a>
</li>
<li class="nav-item">
<a class="nav-link" href="thanks.php">鸣谢</a>
</li>
</ul>
</div>
</div>
</nav>
<?php
// 获取最新公告
$announcementQuery = "SELECT title, content, created_at FROM announcements ORDER BY created_at DESC LIMIT 1";
$announcementResult = $conn->query($announcementQuery);
$announcement = $announcementResult && $announcementResult->num_rows > 0 ? $announcementResult->fetch_assoc() : null;
?>
<?php if ($announcement): ?>
<div class="container mt-3">
<div class="alert alert-info blur-bg">
<h4 class="alert-heading"><?php echo htmlspecialchars($announcement['title']); ?></h4>
<p class="text-muted small">发布时间: <?php echo date('Y-m-d H:i', strtotime($announcement['created_at'])); ?></p>
<div id="announcement-content"><?php echo htmlspecialchars($announcement['content']); ?></div>
<p class="mt-2"><a href="history_announcements.php" class="alert-link">查看所有历史公告</a></p>
</div>
</div>
<?php endif; ?>
<style>
.storage-progress {
margin: 20px 0;
}
.storage-progress .progress-label {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
}
</style>
<div style="align-content: center; text-align: center;">
<div class="container-fluid mt-4 storage-progress">
<?php
// 获取磁盘总容量和网站占用存储
$totalSpace = disk_total_space(__DIR__);
$freeSpace = disk_free_space(__DIR__);
$usedSpace = $totalSpace - $freeSpace;
$usagePercent = round(($usedSpace / $totalSpace) * 100, 2);
function formatBytes($bytes) {
if ($bytes >= 1073741824) {
return round($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
return round($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
return round($bytes / 1024, 2) . ' KB';
} else {
return $bytes . ' B';
}
}
// 计算网站占用量
function getDirectorySize($dir) {
$size = 0;
try {
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
foreach ($iterator as $file) {
try {
if ($file->isFile()) {
$size += $file->getSize();
}
} catch (RuntimeException $e) {
// 跳过无法访问的文件由于open_basedir限制
continue;
}
}
} catch (RuntimeException $e) {
// 处理目录访问错误
error_log('无法访问目录: ' . $e->getMessage());
}
return $size;
}
$websiteSpace = getDirectorySize(__DIR__);
$websiteUsagePercent = round(($websiteSpace / $totalSpace) * 100, 2);
?>
<div class="progress-label">
<span>磁盘使用情况</span>
<span><?php echo formatBytes($usedSpace); ?> / <?php echo formatBytes($totalSpace); ?></span>
</div>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: <?php echo $usagePercent; ?>%" aria-valuenow="<?php echo $usagePercent; ?>" aria-valuemin="0" aria-valuemax="100"><?php echo $usagePercent; ?>%</div>
</div>
<!-- <p>网站占用量: <?php echo formatBytes($websiteSpace); ?></p> -->
<br>
<div class="progress-label">
<span>网站使用情况</span>
<span><?php echo formatBytes($websiteSpace); ?> / <?php echo formatBytes($totalSpace); ?></span>
</div>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: <?php echo $websiteUsagePercent; ?>%" aria-valuenow="<?php echo $websiteUsagePercent; ?>" aria-valuemin="0" aria-valuemax="100"><?php echo $websiteUsagePercent; ?>%</div>
</div>
<!-- <p>网站占用量: <?php echo formatBytes($websiteSpace); ?></p> -->
<br>
<!-- 这里可添加平台信息展示,参考 app.php 样式 -->
<form method="get" action="index.php" class="mb-4" onsubmit="return validateSearch();">
<script>
// 解析公告Markdown内容
document.addEventListener('DOMContentLoaded', function() {
const announcementElement = document.getElementById('announcement-content');
if (announcementElement) {
const markdown = announcementElement.textContent;
announcementElement.innerHTML = marked.parse(markdown);
}
});
function validateSearch() {
const searchInput = document.querySelector('input[name="search"]');
if (searchInput.value.trim() === '') {
Swal.fire({
title: '提示',
text: '请填写搜索名称后再进行搜索!',
icon: 'warning',
confirmButtonText: '确定'
});
return false;
}
return true;
}
</script>
<div class="row g-3">
<div class="col-md-6">
<div class="form-floating">
<input type="text" name="search" class="form-control" id="searchInput" placeholder="搜索应用..." value="<?php echo isset($_GET['search']) ? htmlspecialchars($_GET['search']) : ''; ?>">
<label for="searchInput">搜索应用</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<select name="tag" class="form-select" id="tagSelect">
<option value="">所有标签</option>
<?php
$tagResult = $conn->query("SELECT id, name FROM tags ORDER BY name");
$selectedTag = isset($_GET['tag']) ? $_GET['tag'] : '';
while ($tag = $tagResult->fetch_assoc()):
$selected = ($tag['id'] == $selectedTag) ? 'selected' : '';
?>
<option value="<?php echo $tag['id']; ?>" <?php echo $selected; ?>><?php echo htmlspecialchars($tag['name']); ?></option>
<?php endwhile; ?>
</select>
<label for="tagSelect">选择标签</label>
</div>
</div>
<div class="col-md-2">
<button class="btn btn-primary w-100" style="width: calc(3.5rem + calc(var(--bs-border-width) * 2)); height: calc(3.5rem + calc(var(--bs-border-width) * 2))" type="submit">搜索</button>
</div>
</div>
</form>
<?php if (isset($_SESSION['user_id'])): ?>
<h1>为你推荐</h1>
<div class="row">
<?php
// 获取用户下载过的应用标签
$userId = $_SESSION['user_id'];
$tagSql = "SELECT DISTINCT t.id FROM tags t
JOIN app_tags at ON t.id = at.tag_id
JOIN app_versions av ON at.app_id = av.app_id
JOIN download_history dh ON av.id = dh.version_id
WHERE dh.user_id = ?";
$tagStmt = $conn->prepare($tagSql);
$tagStmt->bind_param('i', $userId);
$tagStmt->execute();
$tagResult = $tagStmt->get_result();
$tagIds = [];
while ($tag = $tagResult->fetch_assoc()) {
$tagIds[] = $tag['id'];
}
$tagStmt->close();
// 获取用户已下载的应用
$downloadedSql = "SELECT DISTINCT a.id FROM apps a
JOIN app_versions av ON a.id = av.app_id
JOIN download_history dh ON av.id = dh.version_id
WHERE dh.user_id = ?";
$downloadedStmt = $conn->prepare($downloadedSql);
$downloadedStmt->bind_param('i', $userId);
$downloadedStmt->execute();
$downloadedResult = $downloadedStmt->get_result();
$downloadedIds = [];
while ($app = $downloadedResult->fetch_assoc()) {
$downloadedIds[] = $app['id'];
}
$downloadedStmt->close();
// 基于标签推荐应用
if (!empty($tagIds)) {
$placeholders = implode(',', array_fill(0, count($tagIds), '?'));
$recommendSql = "SELECT a.id, a.name, a.description, a.age_rating, a.platforms, AVG(r.rating) as avg_rating
FROM apps a
LEFT JOIN reviews r ON a.id = r.app_id
JOIN app_tags at ON a.id = at.app_id
WHERE at.tag_id IN ($placeholders)
AND a.id NOT IN (" . (!empty($downloadedIds) ? implode(',', $downloadedIds) : '0') . ")
AND a.status = 'approved'
GROUP BY a.id
ORDER BY COUNT(at.tag_id) DESC
LIMIT 12";
$recommendStmt = $conn->prepare($recommendSql);
$types = str_repeat('i', count($tagIds));
$recommendStmt->bind_param($types, ...$tagIds);
$recommendStmt->execute();
$recommendResult = $recommendStmt->get_result();
} else {
// 如果没有标签数据,显示热门应用
$recommendSql = "SELECT a.id, a.name, a.description, a.age_rating, AVG(r.rating) as avg_rating, SUM(av.download_count) as total_downloads
FROM apps a
LEFT JOIN reviews r ON a.id = r.app_id
LEFT JOIN app_versions av ON a.id = av.app_id
WHERE a.status = 'approved'
GROUP BY a.id
ORDER BY total_downloads DESC
LIMIT 12";
$recommendResult = $conn->query($recommendSql);
}
if ($recommendResult && $recommendResult->num_rows > 0) {
while ($row = $recommendResult->fetch_assoc()) {
echo '<div class="col-md-3 mb-4">';
echo '<div class="card blur-bg">';
echo '<div class="card-body">';
echo '<h5 class="card-title">'. htmlspecialchars($row['name']) . '</h5>';
echo '<p class="card-text">'. substr(htmlspecialchars($row['description']), 0, 100) . '...</p>';
// 获取应用标签
$tagSql = "SELECT t.name FROM tags t JOIN app_tags at ON t.id = at.tag_id WHERE at.app_id = ?";
$tagStmt = $conn->prepare($tagSql);
$tagStmt->bind_param('i', $row['id']);
$tagStmt->execute();
$tagResult = $tagStmt->get_result();
$tags = [];
while ($tag = $tagResult->fetch_assoc()) {
$tags[] = htmlspecialchars($tag['name']);
}
$tagStmt->close();
// 获取应用适用平台
$platforms = json_decode($row['platforms'], true);
if (!is_array($platforms)) $platforms = [];
echo '<p class="card-text">标签: '. implode(', ', $tags) . '</p>';
echo '<p class="card-text">平台: '. implode(', ', $platforms) . '</p>';
echo '<p class="card-text">评分: '. round($row['avg_rating'] ?? 0, 1) . '/5</p>';
echo '<a href="app.php?id='. $row['id'] . '" class="btn btn-primary">查看详情</a>';
echo '</div></div></div>';
}
} else {
echo '<div class="col-12"><p class="text-center">暂无推荐内容</p></div>';
}
if (isset($recommendStmt)) $recommendStmt->close();
?>
</div>
<?php endif; ?>
<h1>应用列表</h1>
<div class="row" id="app-list">
<!-- 这里将通过PHP动态加载应用列表 -->
<?php
$search = isset($_GET['search']) ? $_GET['search'] : '';
$sql = "SELECT apps.id, apps.name, apps.description, apps.age_rating, apps.platforms, AVG(reviews.rating) as avg_rating
FROM apps
LEFT JOIN reviews ON apps.id = reviews.app_id ";
$conditions = [];
$params = [];
$paramTypes = '';
// 标签筛选
if (!empty($_GET['tag'])) {
$sql .= "JOIN app_tags ON apps.id = app_tags.app_id
JOIN tags ON app_tags.tag_id = tags.id ";
$conditions[] = "app_tags.tag_id = ?";
$tagId = $_GET['tag'];
$params[] = &$tagId;
$paramTypes .= 'i';
}
// 平台筛选
if (!empty($_GET['platform'])) {
// Removed platform condition - column does not exist
$platform = $_GET['platform'];
$params[] = &$platform;
$paramTypes .= 's';
}
// 年龄分级筛选
if (!empty($_GET['age_rating'])) {
$conditions[] = "apps.age_rating = ?";
$ageRating = $_GET['age_rating'];
$params[] = &$ageRating;
$paramTypes .= 's';
}
// 搜索关键词筛选
if (!empty($search)) {
$conditions[] = "(apps.name LIKE ? OR apps.description LIKE ?)";
$searchTerm1 = "%$search%";
$searchTerm2 = "%$search%";
$params[] = &$searchTerm1;
$params[] = &$searchTerm2;
$paramTypes .= 'ss';
}
// 只显示已审核通过的应用
$conditions[] = "apps.status = 'approved'";
// 添加条件
if (!empty($conditions)) {
$sql .= "WHERE " . implode(" AND ", $conditions);
}
$sql .= "GROUP BY apps.id
ORDER BY apps.created_at DESC";
// 执行查询
if (!empty($params)) {
$stmt = $conn->prepare($sql);
if (!$stmt) {
die('预处理语句失败: ' . $conn->error);
}
call_user_func_array([$stmt, 'bind_param'], array_merge([$paramTypes], $params));
if (!$stmt->execute()) {
die('执行语句失败: ' . $stmt->error);
}
$result = $stmt->get_result();
} else {
$result = $conn->query($sql);
if (!$result) {
die('查询失败: ' . $conn->error);
}
}
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo '<div class="col-md-3 mb-4 lazy-load" data-src="app.php?id='. $row['id'] . '">';
echo '<div class="card blur-bg">';
echo '<div class="card-body">';
echo '<h5 class="card-title">'. $row['name'] . '</h5>';
echo '<p class="card-text">'. substr($row['description'], 0, 100) . '...</p>';
// 获取应用标签
$tagSql = "SELECT t.name FROM tags t JOIN app_tags at ON t.id = at.tag_id WHERE at.app_id = ?";
$tagStmt = $conn->prepare($tagSql);
$tagStmt->bind_param('i', $row['id']);
$tagStmt->execute();
$tagResult = $tagStmt->get_result();
$tags = [];
while ($tag = $tagResult->fetch_assoc()) {
$tags[] = htmlspecialchars($tag['name']);
}
$tagStmt->close();
// 获取应用适用平台
$platforms = json_decode($row['platforms'], true);
if (!is_array($platforms)) $platforms = [];
echo '<p class="card-text">标签: '. implode(', ', $tags) . '</p>';
echo '<p class="card-text">平台: '. implode(', ', $platforms) . '</p>';
echo '<p class="card-text">评分: '. round($row['avg_rating'], 1) . '/5</p>';
echo '<a href="app.php?id='. $row['id'] . '" class="btn btn-primary">查看详情</a>';
echo '</div>';
echo '</div>';
echo '</div>';
}
}
?>
<script>
document.addEventListener("DOMContentLoaded", function() {
const lazyLoadItems = document.querySelectorAll(".lazy-load");
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 这里可以添加加载动画或其他操作
observer.unobserve(entry.target);
}
});
});
lazyLoadItems.forEach((item) => {
observer.observe(item);
});
});
</script>
<?php
?>
</div>
</div></div>
<!-- Bootstrap JS Bundle with Popper -->
<script src="/js/bootstrap.bundle.js"></script>
<script>
// 导航栏滚动效果
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 10) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('page-transition');
});
</script>
</body>
</html>

5977
js/all.js Normal file

File diff suppressed because one or more lines are too long

6
js/all.min.js vendored Normal file

File diff suppressed because one or more lines are too long

7
js/bootstrap.bundle.js vendored Normal file

File diff suppressed because one or more lines are too long

749
js/brands.js Normal file

File diff suppressed because one or more lines are too long

6
js/brands.min.js vendored Normal file

File diff suppressed because one or more lines are too long

3477
js/charts.js Normal file

File diff suppressed because it is too large Load Diff

1138
js/conflict-detection.js Normal file

File diff suppressed because it is too large Load Diff

6
js/conflict-detection.min.js vendored Normal file

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More