├── 1.png
├── 2.png
├── 3.png
├── LICENSE
├── README.md
├── admin
├── audits.php
├── categories.php
├── index.php
├── logs.php
├── models.php
├── softwares.php
└── utils.php
├── ajax_softwares.php
├── assets
└── css
│ └── custom.css
├── db.php
├── down.sql
├── download.php
├── index.php
├── login.php
├── logout.php
└── register.php
/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xixihucxixi/SoftShareweb/34db1cd86a2dff1dee2c61113a2f9c3e0687a881/1.png
--------------------------------------------------------------------------------
/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xixihucxixi/SoftShareweb/34db1cd86a2dff1dee2c61113a2f9c3e0687a881/2.png
--------------------------------------------------------------------------------
/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xixihucxixi/SoftShareweb/34db1cd86a2dff1dee2c61113a2f9c3e0687a881/3.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 xixihucxixi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SoftShareWeb
2 |
3 | 一个简单实用的【PHP+MySQL】开源软件下载与管理平台,具备用户注册/登录、软件下载与分类、软件上传与后台审批、操作日志、AJAX体验美化等功能。适合高校实验/公司内部资料共享、开源会议资源分发及个人学习进阶使用。
4 | ## 项目截图
5 |
6 | 主页演示:
7 |
8 | 
9 | ## 特性
10 |
11 | - 用户免登录浏览,注册后可下载软件资源(支持多用户)
12 | - 软件支持分类、型号管理,支持关键字全局搜索及AJAX无刷新切换
13 | - 管理员后台:具有软件上传/审核、分类型号管理、上传下载日志、权限多级
14 | - 支持软件包文件上传进度条,体验丝滑
15 | - 所有密码采用安全哈希算法存储
16 | - 界面基于 Bootstrap4,简洁美观,移动端友好
17 |
18 | ## 安装
19 |
20 | 1. **克隆代码**
21 |
22 | ```bash
23 | git clone https://github.com/你的用户名/SoftShareHub.git
24 | cd SoftShareWeb
25 |
26 | 2.导入数据库结构
27 | 使用 phpMyAdmin 或 CLI 导入 docs/database.sql 文件(你需备好建表SQL)。
28 |
29 | 3.配置数据库
30 |
31 | 修改 db.php 文件,填写你的数据库主机、用户名、密码和库名。
32 |
33 | 4.确保 uploads/downloads 目录有可写权限
34 | chmod -R 755 downloads
35 | 首次管理员账号
36 |
37 | 5.默认内置:请注册好之后手动修改is_admin为1就是管理员了
38 | 主要目录结构
39 | assets/ # 前端CSS/JS及图片
40 | admin/ # 后台管理代码
41 | downloads/ # 软件包实际存储目录
42 | index.php # 前台首页/列表
43 | login.php # 登录
44 | register.php # 注册
45 | download.php # 下载逻辑(登录校验)
46 | db.php # 数据库连接
47 | ...
48 |
49 | 常见问题
50 | 上传失败?
51 | 检查 PHP 配置中的 upload_max_filesize 和 post_max_size
52 | 进度条无效?
53 | 用 Chrome/Edge/Firefox 等现代浏览器
54 | 登录失败?
55 | 检查数据库密码字段是否为 password_hash 值,不是明文
56 | 贡献 & 扩展
57 | 欢迎 PR,任何问题也可直接提 Issue~
58 |
59 | 许可证
60 | MIT
61 |
62 | 致谢
63 | 本项目为开源练习范例,适当参考、二次开发请注明原作者及项目来源。
64 |
65 |
--------------------------------------------------------------------------------
/admin/audits.php:
--------------------------------------------------------------------------------
1 | prepare("UPDATE softwares SET is_audited=1 WHERE id=?")->execute([$id]);
9 | header('Location: audits.php');
10 | exit();
11 | }
12 | // 驳回(删除)
13 | if (isset($_GET['reject'])) {
14 | $id = intval($_GET['reject']);
15 | $stmt = $pdo->prepare("SELECT filename FROM softwares WHERE id=?");
16 | $stmt->execute([$id]);
17 | $sw = $stmt->fetch();
18 | if ($sw) @unlink("../downloads/".$sw['filename']);
19 | $pdo->prepare("DELETE FROM softwares WHERE id=?")->execute([$id]);
20 | header('Location: audits.php');
21 | exit();
22 | }
23 | // 查询未审核
24 | $list = $pdo->query("SELECT s.*, c.name categ, m.name model
25 | FROM softwares s
26 | LEFT JOIN categories c ON s.category_id=c.id
27 | LEFT JOIN models m ON s.model_id=m.id
28 | WHERE s.is_audited=0 ORDER BY s.uploaded_at DESC")->fetchAll();
29 | ?>
30 |
31 |
32 |
33 |
34 | 软件审核
35 |
36 |
37 |
38 |
39 |
40 |
⬅ 返回后台
41 |
待审核软件
42 |
43 | 名称 分类 型号 描述 上传时间 操作
44 |
45 |
46 | =htmlspecialchars($r['name'])?>
47 | =htmlspecialchars($r['categ'])?>
48 | =htmlspecialchars($r['model'])?>
49 | =htmlspecialchars($r['description'])?>
50 | =$r['uploaded_at']?>
51 |
52 | 通过
53 | 驳回
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/admin/categories.php:
--------------------------------------------------------------------------------
1 | prepare("INSERT IGNORE INTO categories (name) VALUES (?)");
10 | $stmt->execute([$name]);
11 | }
12 | header('Location: categories.php');
13 | exit();
14 | }
15 | // 删除
16 | if (isset($_GET['delete'])) {
17 | $id = intval($_GET['delete']);
18 | $pdo->prepare("DELETE FROM categories WHERE id=?")->execute([$id]);
19 | header('Location: categories.php');
20 | exit();
21 | }
22 | $list = $pdo->query("SELECT * FROM categories ORDER BY id")->fetchAll();
23 | ?>
24 |
25 |
26 |
27 | 分类管理
28 |
29 |
30 |
31 |
32 |
33 |
⬅ 返回后台
34 |
分类管理
35 |
39 |
40 | ID 名称 操作
41 |
42 |
43 | =$r['id']?>
44 | =htmlspecialchars($r['name'])?>
45 | 删除
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/admin/index.php:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 | 后台管理中心
13 |
14 |
15 |
16 |
17 |
18 | 后台管理
19 | 管理员:=htmlspecialchars($_SESSION['username'])?>
20 | 回前台
21 |
22 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/admin/logs.php:
--------------------------------------------------------------------------------
1 | query("SELECT l.*, u.username, s.name soft
5 | FROM upload_logs l LEFT JOIN users u ON l.user_id=u.id LEFT JOIN softwares s ON l.software_id=s.id
6 | ORDER BY l.upload_time DESC LIMIT 200")->fetchAll();
7 | $downlog = $pdo->query("SELECT l.*, u.username, s.name soft
8 | FROM download_logs l LEFT JOIN users u ON l.user_id=u.id LEFT JOIN softwares s ON l.software_id=s.id
9 | ORDER BY l.download_time DESC LIMIT 200")->fetchAll();
10 | ?>
11 |
12 |
13 |
14 |
15 | 上传/下载日志
16 |
17 |
18 |
19 |
20 |
21 |
⬅ 返回后台
22 |
最近上传日志
23 |
24 | 用户 软件 上传时间
25 |
26 | =htmlspecialchars($r['username'])?> =htmlspecialchars($r['soft'])?> =$r['upload_time']?>
27 |
28 |
29 |
最近下载日志
30 |
31 | 用户 软件 下载时间
32 |
33 | =htmlspecialchars($r['username'])?> =htmlspecialchars($r['soft'])?> =$r['download_time']?>
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/admin/models.php:
--------------------------------------------------------------------------------
1 | query("SELECT * FROM categories ORDER BY id")->fetchAll();
5 | // 新增
6 | if ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['name']) && isset($_POST['category_id'])) {
7 | $name=trim($_POST['name']);
8 | $cid=intval($_POST['category_id']);
9 | if ($name && $cid) {
10 | $stmt = $pdo->prepare("INSERT INTO models (category_id, name) VALUES (?,?)");
11 | $stmt->execute([$cid, $name]);
12 | }
13 | header('Location: models.php');
14 | exit();
15 | }
16 | // 删除
17 | if (isset($_GET['delete'])) {
18 | $id = intval($_GET['delete']);
19 | $pdo->prepare("DELETE FROM models WHERE id=?")->execute([$id]);
20 | header('Location: models.php');
21 | exit();
22 | }
23 | $list = $pdo->query("SELECT m.*, c.name cname FROM models m LEFT JOIN categories c ON m.category_id=c.id ORDER BY m.category_id, m.id")->fetchAll();
24 | ?>
25 |
26 |
27 |
28 | 型号管理
29 |
30 |
31 |
32 |
33 |
34 |
⬅ 返回后台
35 |
型号管理
36 |
43 |
44 | ID 所属分类 型号名称 操作
45 |
46 |
47 | =$r['id']?>
48 | =htmlspecialchars($r['cname'])?>
49 | =htmlspecialchars($r['name'])?>
50 | 删除
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/admin/softwares.php:
--------------------------------------------------------------------------------
1 | query("SELECT * FROM categories ORDER BY id")->fetchAll();
10 | $mdl_map = [];
11 | foreach ($cats as $cat) {
12 | $mdl_map[$cat['id']] = $pdo->query("SELECT * FROM models WHERE category_id={$cat['id']}")->fetchAll();
13 | }
14 |
15 | // 上传功能
16 | if ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['op']) && $_POST['op']=='add') {
17 | $name = trim($_POST['name']);
18 | $description = trim($_POST['description']);
19 | $category_id = intval($_POST['category_id']);
20 | $model_id = intval($_POST['model_id']);
21 | $file = $_FILES['file'];
22 | if ($file['error'] > 0 || !$file['name']) $err = "请选择上传文件";
23 | else {
24 | $tmp = $file['tmp_name'];
25 | $filename = date('YmdHis').'_'.uniqid().strrchr($file['name'], '.');
26 | $hash = hash_file('sha256', $tmp);
27 | $cstmt = $pdo->prepare("SELECT id FROM softwares WHERE category_id=? AND model_id=? AND (filehash=?)");
28 | $cstmt->execute([$category_id, $model_id, $hash]);
29 | if ($cstmt->fetch()) $err = "该文件已存在";
30 | else {
31 | move_uploaded_file($tmp, "../downloads/$filename");
32 | $stmt = $pdo->prepare("INSERT INTO softwares (category_id, model_id, name, description, filename, filehash, uploader_id, is_audited) VALUES (?,?,?,?,?,?,?,1)");
33 | $stmt->execute([$category_id, $model_id, $name, $description, $filename, $hash, $_SESSION['user_id']]);
34 | $succ = "上传成功";
35 | }
36 | }
37 | // 如果是AJAX上传,直接简明输出
38 | if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
39 | if (!empty($succ)) echo ''.$succ.' ';
40 | else echo ''.$err.' ';
41 | exit;
42 | }
43 | }
44 |
45 | // 删除功能
46 | if (isset($_GET['delete'])) {
47 | $id = intval($_GET['delete']);
48 | $stmt = $pdo->prepare("SELECT filename FROM softwares WHERE id=?");
49 | $stmt->execute([$id]);
50 | $sw = $stmt->fetch();
51 | if ($sw) @unlink("../downloads/".$sw['filename']);
52 | $stmt = $pdo->prepare("DELETE FROM softwares WHERE id=?");
53 | $stmt->execute([$id]);
54 | header('Location: softwares.php');
55 | exit();
56 | }
57 |
58 | // 编辑功能
59 | if ($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['op']) && $_POST['op']=='edit') {
60 | $id = intval($_POST['id']);
61 | $name = trim($_POST['name']);
62 | $description = trim($_POST['description']);
63 | $category_id = intval($_POST['category_id']);
64 | $model_id = intval($_POST['model_id']);
65 | $stmt = $pdo->prepare("UPDATE softwares SET name=?,description=?, category_id=?, model_id=? WHERE id=?");
66 | $stmt->execute([$name, $description, $category_id, $model_id, $id]);
67 | header("Location: softwares.php");
68 | exit();
69 | }
70 |
71 | // 列表展示
72 | $list = $pdo->query("SELECT s.*, c.name categ, m.name model
73 | FROM softwares s
74 | LEFT JOIN categories c ON s.category_id=c.id
75 | LEFT JOIN models m ON s.model_id=m.id
76 | ORDER BY s.uploaded_at DESC")->fetchAll();
77 |
78 | ?>
79 |
80 |
81 |
82 |
83 | 软件管理与上传
84 |
85 |
86 |
91 |
92 |
93 |
94 |
95 | ⬅ 返回后台首页
96 |
97 |
软件管理
98 | +上传
99 |
100 |
101 |
102 |
103 |
104 | ID 名称 分类 型号 描述 上传时间 操作
105 |
106 |
107 |
108 |
109 | =$r['id']?>
110 | =htmlspecialchars($r['name'])?>
111 | =htmlspecialchars($r['categ'])?>
112 | =htmlspecialchars($r['model'])?>
113 | =htmlspecialchars($r['description'])?>
114 | =$r['uploaded_at']?>
115 |
116 | 编辑
117 | 删除
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
163 |
164 |
165 | query("SELECT * FROM softwares WHERE id=$eid")->fetch();
168 | ?>
169 |
170 |
171 |
172 |
173 |
174 |
175 | 软件名称
176 |
177 |
178 | 软件描述
179 | =htmlspecialchars($sw['description'])?>
180 |
181 |
182 | 分类
183 |
184 |
185 | >=$c['name']?>
186 |
187 |
188 |
189 | 型号
190 |
191 |
192 | >=$mod['name']?>
193 |
194 |
195 |
196 | 保存
197 | 取消
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
260 |
261 |
--------------------------------------------------------------------------------
/admin/utils.php:
--------------------------------------------------------------------------------
1 | prepare("SELECT * FROM models WHERE category_id=? ORDER BY id");
5 | $stmt->execute([$cat_id]);
6 | return $stmt->fetchAll();
7 | }
8 |
9 | // 获取所有分类
10 | function get_all_categories($pdo) {
11 | return $pdo->query("SELECT * FROM categories")->fetchAll();
12 | }
13 |
14 | // 安全输出
15 | function h($str) {
16 | return htmlspecialchars($str, ENT_QUOTES);
17 | }
18 | ?>
--------------------------------------------------------------------------------
/ajax_softwares.php:
--------------------------------------------------------------------------------
1 | prepare(
10 | "SELECT s.*, m.name modelname FROM softwares s
11 | LEFT JOIN models m ON s.model_id=m.id
12 | WHERE (s.name LIKE ? OR s.description LIKE ?) AND s.is_audited=1
13 | ORDER BY s.uploaded_at DESC"
14 | );
15 | $stmt->execute(['%'.$kw.'%', '%'.$kw.'%']);
16 | } else {
17 | $cid = intval($_GET['catid']??0);
18 | $stmt = $pdo->prepare(
19 | "SELECT s.*, m.name modelname FROM softwares s
20 | LEFT JOIN models m ON s.model_id=m.id
21 | WHERE s.category_id=? AND s.is_audited=1
22 | ORDER BY s.uploaded_at DESC"
23 | );
24 | $stmt->execute([$cid]);
25 | }
26 | $softs = $stmt->fetchAll(PDO::FETCH_ASSOC);
27 | ?>
28 |
29 | 暂无匹配的软件数据。
30 |
31 |
32 |
33 |
34 |
35 |
36 |
=htmlspecialchars($sw['name'])?>
37 |
38 | 型号:=htmlspecialchars($sw['modelname'])?> 更新时间:=htmlspecialchars($sw['uploaded_at'])?>
39 |
40 |
=htmlspecialchars($sw['description'])?>
41 |
42 |
下载
43 |
44 |
登录后下载
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/assets/css/custom.css:
--------------------------------------------------------------------------------
1 | /* 通用背景和字体微调 */
2 | body {
3 | background-color: #f8f9fa;
4 | font-family: "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
5 | color: #222;
6 | }
7 |
8 | /* 顶部导航栏LOGO与标题 */
9 | .navbar-brand {
10 | font-weight: bold;
11 | font-size: 22px;
12 | letter-spacing: 2px;
13 | }
14 |
15 | /* 左侧分类栏 */
16 | .sidebar {
17 | background: #fff;
18 | border-radius: 5px;
19 | box-shadow: 0 1px 8px rgba(0,0,0,0.04);
20 | }
21 | .list-group-item.category-active {
22 | background: #007bff !important;
23 | color: #fff;
24 | font-weight: bold;
25 | border-color: #003d80;
26 | }
27 |
28 | /* 卡片美化 */
29 | .card {
30 | border-radius: 4px;
31 | box-shadow: 0 2px 12px rgba(0,0,0,0.07);
32 | }
33 | .card-title {
34 | font-size: 18px;
35 | font-weight: bold;
36 | }
37 |
38 | /* 上传弹窗 */
39 | #addform {
40 | box-shadow: 0 4px 18px rgba(0,0,0,0.15);
41 | border-radius: 7px;
42 | }
43 |
44 | /* 编辑弹窗覆盖层 */
45 | .modal {
46 | z-index: 9999;
47 | }
48 |
49 | /* 上传进度条(Bootstrap风格) */
50 | .progress {
51 | height: 25px;
52 | border-radius: 6px;
53 | background: #e9ecef;
54 | }
55 | .progress-bar {
56 | font-weight: bold;
57 | background-color: #4caf50 !important;
58 | color: #fff;
59 | transition: width 0.3s;
60 | }
61 |
62 | /* 上传/下载按钮特效 */
63 | .download-btn, .btn-success {
64 | color: #fff !important;
65 | background: #28a745;
66 | border: none;
67 | padding: 4px 16px;
68 | border-radius: 3px;
69 | font-size: 15px;
70 | }
71 | .download-btn:hover, .btn-success:hover {
72 | background: #218838 !important;
73 | }
74 |
75 | /* 错误/警告提示 */
76 | .alert {
77 | font-size: 0.97rem;
78 | border-radius: 5px;
79 | }
80 | .alert-danger {
81 | background: #ffe5e8;
82 | border-color: #f5c2c7;
83 | color: #fa2848;
84 | }
85 | .alert-success {
86 | background: #ebfaeb;
87 | border-color: #a2dca2;
88 | color: #258825;
89 | }
90 | .alert-warning {
91 | background: #fff6e3;
92 | border-color: #ffe885;
93 | color: #946200;
94 | }
95 |
96 | /* form居中小巧卡片 */
97 | .login-box, .register-box {
98 | max-width: 380px;
99 | margin: 60px auto;
100 | }
101 | .card-header {
102 | font-size: 20px;
103 | }
104 | .form-control:focus {
105 | border-color: #0099ff;
106 | box-shadow: 0 0 4px #9fd9ff;
107 | }
108 |
109 | /* 表格美化 */
110 | .table th {
111 | background: #e9ecef;
112 | }
113 |
114 | @media (max-width: 768px) {
115 | .sidebar { min-width:inherit; }
116 | .card { box-shadow: none;}
117 | #addform { left:10px; right:10px; margin-left:0; width:auto;}
118 | }
119 |
--------------------------------------------------------------------------------
/db.php:
--------------------------------------------------------------------------------
1 | PDO::ERRMODE_EXCEPTION,
11 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
12 | PDO::ATTR_EMULATE_PREPARES => false,
13 | ];
14 | try {
15 | $pdo = new PDO($dsn, $db_user, $db_pass, $options);
16 | } catch (PDOException $e) {
17 | exit('数据库连接失败: ' . $e->getMessage());
18 | }
19 |
20 | // 启动 session
21 | if (session_status() == PHP_SESSION_NONE) {
22 | session_start();
23 | }
24 |
25 | // 如果有登录用户,便捷补充用户名 session 字段
26 | if (!empty($_SESSION['user_id']) && empty($_SESSION['username'])) {
27 | // 下方代码可选,用于在登录后刷新页面时 $_SESSION['username'] 始终存在
28 | $stmt = $pdo->prepare('SELECT username FROM users WHERE id=?');
29 | $stmt->execute([$_SESSION['user_id']]);
30 | if ($row = $stmt->fetch()) {
31 | $_SESSION['username'] = $row['username'];
32 | }
33 | }
34 | ?>
35 |
--------------------------------------------------------------------------------
/down.sql:
--------------------------------------------------------------------------------
1 | -- phpMyAdmin SQL Dump
2 | -- version 5.1.1
3 | -- https://www.phpmyadmin.net/
4 | --
5 | -- 主机: localhost
6 | -- 生成日期: 2025-04-29 20:14:48
7 | -- 服务器版本: 5.7.43-log
8 | -- PHP 版本: 8.0.26
9 |
10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
11 | START TRANSACTION;
12 | SET time_zone = "+00:00";
13 |
14 |
15 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
16 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
17 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
18 | /*!40101 SET NAMES utf8mb4 */;
19 |
20 | --
21 | -- 数据库: `down`
22 | --
23 |
24 | -- --------------------------------------------------------
25 |
26 | --
27 | -- 表的结构 `admin_logs`
28 | --
29 |
30 | CREATE TABLE `admin_logs` (
31 | `id` int(10) UNSIGNED NOT NULL,
32 | `admin_id` int(10) UNSIGNED DEFAULT NULL,
33 | `action` varchar(255) NOT NULL,
34 | `log_time` datetime DEFAULT CURRENT_TIMESTAMP
35 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
36 |
37 | -- --------------------------------------------------------
38 |
39 | --
40 | -- 表的结构 `categories`
41 | --
42 |
43 | CREATE TABLE `categories` (
44 | `id` int(10) UNSIGNED NOT NULL,
45 | `name` varchar(100) NOT NULL
46 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
47 |
48 | -- --------------------------------------------------------
49 |
50 | --
51 | -- 表的结构 `download_logs`
52 | --
53 |
54 | CREATE TABLE `download_logs` (
55 | `id` int(10) UNSIGNED NOT NULL,
56 | `user_id` int(10) UNSIGNED NOT NULL,
57 | `software_id` int(10) UNSIGNED NOT NULL,
58 | `download_time` datetime DEFAULT CURRENT_TIMESTAMP
59 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
60 |
61 | -- --------------------------------------------------------
62 |
63 | --
64 | -- 表的结构 `models`
65 | --
66 |
67 | CREATE TABLE `models` (
68 | `id` int(10) UNSIGNED NOT NULL,
69 | `category_id` int(10) UNSIGNED NOT NULL,
70 | `name` varchar(100) NOT NULL
71 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
72 |
73 | -- --------------------------------------------------------
74 |
75 | --
76 | -- 表的结构 `softwares`
77 | --
78 |
79 | CREATE TABLE `softwares` (
80 | `id` int(10) UNSIGNED NOT NULL,
81 | `category_id` int(10) UNSIGNED NOT NULL,
82 | `model_id` int(10) UNSIGNED NOT NULL,
83 | `name` varchar(100) NOT NULL,
84 | `description` text,
85 | `filename` varchar(255) NOT NULL,
86 | `filehash` varchar(64) DEFAULT NULL,
87 | `uploader_id` int(10) UNSIGNED NOT NULL,
88 | `uploaded_at` datetime DEFAULT CURRENT_TIMESTAMP,
89 | `is_audited` tinyint(1) DEFAULT '0'
90 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
91 |
92 | -- --------------------------------------------------------
93 |
94 | --
95 | -- 表的结构 `upload_logs`
96 | --
97 |
98 | CREATE TABLE `upload_logs` (
99 | `id` int(10) UNSIGNED NOT NULL,
100 | `user_id` int(10) UNSIGNED NOT NULL,
101 | `software_id` int(10) UNSIGNED NOT NULL,
102 | `upload_time` datetime DEFAULT CURRENT_TIMESTAMP
103 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
104 |
105 | -- --------------------------------------------------------
106 |
107 | --
108 | -- 表的结构 `users`
109 | --
110 |
111 | CREATE TABLE `users` (
112 | `id` int(10) UNSIGNED NOT NULL,
113 | `username` varchar(100) NOT NULL,
114 | `password` varchar(255) NOT NULL,
115 | `is_admin` tinyint(1) DEFAULT '0',
116 | `created_at` datetime DEFAULT CURRENT_TIMESTAMP
117 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
118 |
119 | --
120 | -- 转储表的索引
121 | --
122 |
123 | --
124 | -- 表的索引 `admin_logs`
125 | --
126 | ALTER TABLE `admin_logs`
127 | ADD PRIMARY KEY (`id`),
128 | ADD KEY `admin_id` (`admin_id`);
129 |
130 | --
131 | -- 表的索引 `categories`
132 | --
133 | ALTER TABLE `categories`
134 | ADD PRIMARY KEY (`id`),
135 | ADD UNIQUE KEY `name` (`name`);
136 |
137 | --
138 | -- 表的索引 `download_logs`
139 | --
140 | ALTER TABLE `download_logs`
141 | ADD PRIMARY KEY (`id`),
142 | ADD KEY `user_id` (`user_id`),
143 | ADD KEY `software_id` (`software_id`);
144 |
145 | --
146 | -- 表的索引 `models`
147 | --
148 | ALTER TABLE `models`
149 | ADD PRIMARY KEY (`id`),
150 | ADD KEY `category_id` (`category_id`);
151 |
152 | --
153 | -- 表的索引 `softwares`
154 | --
155 | ALTER TABLE `softwares`
156 | ADD PRIMARY KEY (`id`),
157 | ADD KEY `category_id` (`category_id`),
158 | ADD KEY `model_id` (`model_id`),
159 | ADD KEY `uploader_id` (`uploader_id`);
160 |
161 | --
162 | -- 表的索引 `upload_logs`
163 | --
164 | ALTER TABLE `upload_logs`
165 | ADD PRIMARY KEY (`id`),
166 | ADD KEY `user_id` (`user_id`),
167 | ADD KEY `software_id` (`software_id`);
168 |
169 | --
170 | -- 表的索引 `users`
171 | --
172 | ALTER TABLE `users`
173 | ADD PRIMARY KEY (`id`),
174 | ADD UNIQUE KEY `username` (`username`);
175 |
176 | --
177 | -- 在导出的表使用AUTO_INCREMENT
178 | --
179 |
180 | --
181 | -- 使用表AUTO_INCREMENT `admin_logs`
182 | --
183 | ALTER TABLE `admin_logs`
184 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
185 |
186 | --
187 | -- 使用表AUTO_INCREMENT `categories`
188 | --
189 | ALTER TABLE `categories`
190 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
191 |
192 | --
193 | -- 使用表AUTO_INCREMENT `download_logs`
194 | --
195 | ALTER TABLE `download_logs`
196 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
197 |
198 | --
199 | -- 使用表AUTO_INCREMENT `models`
200 | --
201 | ALTER TABLE `models`
202 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
203 |
204 | --
205 | -- 使用表AUTO_INCREMENT `softwares`
206 | --
207 | ALTER TABLE `softwares`
208 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
209 |
210 | --
211 | -- 使用表AUTO_INCREMENT `upload_logs`
212 | --
213 | ALTER TABLE `upload_logs`
214 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
215 |
216 | --
217 | -- 使用表AUTO_INCREMENT `users`
218 | --
219 | ALTER TABLE `users`
220 | MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;
221 |
222 | --
223 | -- 限制导出的表
224 | --
225 |
226 | --
227 | -- 限制表 `admin_logs`
228 | --
229 | ALTER TABLE `admin_logs`
230 | ADD CONSTRAINT `admin_logs_ibfk_1` FOREIGN KEY (`admin_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;
231 |
232 | --
233 | -- 限制表 `download_logs`
234 | --
235 | ALTER TABLE `download_logs`
236 | ADD CONSTRAINT `download_logs_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
237 | ADD CONSTRAINT `download_logs_ibfk_2` FOREIGN KEY (`software_id`) REFERENCES `softwares` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
238 |
239 | --
240 | -- 限制表 `models`
241 | --
242 | ALTER TABLE `models`
243 | ADD CONSTRAINT `models_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
244 |
245 | --
246 | -- 限制表 `softwares`
247 | --
248 | ALTER TABLE `softwares`
249 | ADD CONSTRAINT `softwares_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
250 | ADD CONSTRAINT `softwares_ibfk_2` FOREIGN KEY (`model_id`) REFERENCES `models` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
251 | ADD CONSTRAINT `softwares_ibfk_3` FOREIGN KEY (`uploader_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
252 |
253 | --
254 | -- 限制表 `upload_logs`
255 | --
256 | ALTER TABLE `upload_logs`
257 | ADD CONSTRAINT `upload_logs_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
258 | ADD CONSTRAINT `upload_logs_ibfk_2` FOREIGN KEY (`software_id`) REFERENCES `softwares` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
259 | COMMIT;
260 |
261 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
262 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
263 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
264 |
--------------------------------------------------------------------------------
/download.php:
--------------------------------------------------------------------------------
1 | prepare("SELECT * FROM softwares WHERE id=? AND is_audited=1");
17 | $stmt->execute([$id]);
18 | $sw = $stmt->fetch();
19 | if (!$sw) {
20 | die('该软件不存在或未通过审核!');
21 | }
22 |
23 | $filepath = __DIR__ . '/downloads/' . $sw['filename'];
24 | if (!is_file($filepath)) {
25 | die('文件已被删除或找不到!');
26 | }
27 |
28 | // 记录日志
29 | $stmt = $pdo->prepare("INSERT INTO download_logs (user_id, software_id) VALUES (?, ?)");
30 | $stmt->execute([$_SESSION['user_id'], $id]);
31 |
32 | // 输出下载
33 | header('Content-Description: File Transfer');
34 | header('Content-Type: application/octet-stream');
35 | header('Content-Disposition: attachment; filename="' . rawurlencode($sw['filename']) . '"');
36 | header('Content-Length: ' . filesize($filepath));
37 | header('Expires: 0');
38 | header('Cache-Control: must-revalidate');
39 | header('Pragma: public');
40 | readfile($filepath);
41 | exit;
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | query("SELECT * FROM categories ORDER BY id")->fetchAll(PDO::FETCH_ASSOC);
5 | $first_cid = $categories ? $categories[0]['id'] : 0;
6 | $login = !empty($_SESSION['user_id']);
7 | ?>
8 |
9 |
10 |
11 |
12 | 软件下载中心
13 |
14 |
15 |
18 |
19 |
20 |
21 | 软件下载平台
22 |
23 |
24 |
你好,=htmlspecialchars($_SESSION['username']);?>
25 |
退出
26 |
27 |
登录
28 |
注册
29 |
30 |
31 |
32 |
33 |
34 |
35 |
46 |
47 |
48 |
49 |
50 |
51 | 搜索
52 | 清空
53 |
54 |
软件下载列表
55 |
58 |
59 |
60 |
61 |
62 |
114 |
115 |
--------------------------------------------------------------------------------
/login.php:
--------------------------------------------------------------------------------
1 | prepare("SELECT * FROM users WHERE username = ?");
13 | $stmt->execute([$username]);
14 | $user = $stmt->fetch();
15 | if ($user && password_verify($password, $user['password'])) {
16 | // 写入Session
17 | $_SESSION['user_id'] = $user['id'];
18 | $_SESSION['is_admin'] = $user['is_admin'];
19 | $_SESSION['username'] = $user['username'];
20 | header('Location: index.php');
21 | exit();
22 | } else {
23 | $error = '用户名或密码错误!';
24 | }
25 | }
26 | }
27 | ?>
28 |
29 |
30 |
31 |
32 | 用户登录
33 |
34 |
38 |
39 |
40 |
41 |
42 |
45 |
46 |
47 |
=htmlspecialchars($error)?>
48 |
49 |
50 |
51 | 用户名
52 |
53 |
54 |
55 | 密码
56 |
57 |
58 | 登录
59 |
60 |
61 |
没有账号?用户注册
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/logout.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/register.php:
--------------------------------------------------------------------------------
1 | 20) {
14 | $error = '用户名长度需在3到20字符之间。';
15 | } elseif ($password !== $password2) {
16 | $error = '两次输入的密码不一致!';
17 | } elseif (strlen($password) < 6) {
18 | $error = '密码长度不能少于6位。';
19 | } else {
20 | // 检查用户是否存在
21 | $stmt = $pdo->prepare("SELECT id FROM users WHERE username=?");
22 | $stmt->execute([$username]);
23 | if ($stmt->fetch()) {
24 | $error = '该用户名已被注册!';
25 | } else {
26 | // 写入新用户
27 | $hash = password_hash($password, PASSWORD_DEFAULT);
28 | $stmt = $pdo->prepare("INSERT INTO users (username, password, is_admin) VALUES (?, ?, 0)");
29 | $stmt->execute([$username, $hash]);
30 | header('Location: login.php');
31 | exit();
32 | }
33 | }
34 | }
35 | ?>
36 |
37 |
38 |
39 |
40 | 用户注册
41 |
42 |
46 |
47 |
48 |
49 |
50 |
53 |
54 |
55 |
=htmlspecialchars($error)?>
56 |
57 |
58 |
59 | 用户名
60 |
62 |
63 |
64 | 密码
65 |
67 |
68 |
69 | 重复密码
70 |
72 |
73 | 注册
74 |
75 |
76 |
已有账号?立即登录
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------