湛江有人做网站 的吗/拉新任务接单放单平台
说明:
我计划用fastapi+angular做一款停车管理系统,支持跨域
1.设计mysql数据库表,
2.建表,添加测试数据,多表查询,
3.在fastapi写接口查询数据,
4.用postman测试,
5.在angular前端展示
效果图:
用fastapi写4个查询接口
1.查询具体用户的所有停车记录
2.查询当前正在停车的记录,显示车牌,车主姓名,进入时间,车位位置
3.查询所有已结束停车的费用明细表,详细列举车型和费率
4.统计停车场车位的利用率和空闲率
step1:sql
-- 1. 建表语句(6张表)
-- --------------------------------------------------------
-- 用户表
CREATE TABLE users (user_id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(50) NOT NULL,phone VARCHAR(20) UNIQUE,email VARCHAR(100) UNIQUE,reg_date DATETIME DEFAULT CURRENT_TIMESTAMP
);-- 车辆表
CREATE TABLE vehicles (vehicle_id INT AUTO_INCREMENT PRIMARY KEY,user_id INT NOT NULL,license_plate VARCHAR(20) UNIQUE NOT NULL,type ENUM('轿车','SUV','卡车','电动车','摩托车'),FOREIGN KEY (user_id) REFERENCES users(user_id)
);-- 车位表
CREATE TABLE parking_spaces (space_id INT AUTO_INCREMENT PRIMARY KEY,zone VARCHAR(10) NOT NULL, -- 区域(A区/B区等)number VARCHAR(10) NOT NULL, -- 车位编号status ENUM('AVAILABLE','OCCUPIED','MAINTENANCE') DEFAULT 'AVAILABLE',UNIQUE(zone, number)
);-- 收费规则表
CREATE TABLE fee_rules (rule_id INT AUTO_INCREMENT PRIMARY KEY,vehicle_type ENUM('轿车','SUV','卡车','电动车','摩托车'),hourly_rate DECIMAL(8,2) NOT NULL,effective_date DATE NOT NULL
);-- 停车记录表
CREATE TABLE parking_records (record_id INT AUTO_INCREMENT PRIMARY KEY,vehicle_id INT NOT NULL,space_id INT NOT NULL,entry_time DATETIME NOT NULL,exit_time DATETIME,fee DECIMAL(10,2),FOREIGN KEY (vehicle_id) REFERENCES vehicles(vehicle_id),FOREIGN KEY (space_id) REFERENCES parking_spaces(space_id)
);-- 支付记录表(新增)
CREATE TABLE payment_records (payment_id INT AUTO_INCREMENT PRIMARY KEY,record_id INT NOT NULL,amount DECIMAL(10,2) NOT NULL,payment_time DATETIME DEFAULT CURRENT_TIMESTAMP,payment_method ENUM('支付宝','微信','现金','信用卡'),status ENUM('PAID','UNPAID'),FOREIGN KEY (record_id) REFERENCES parking_records(record_id)
);-- 2. 模拟数据(每表至少10条)
-- --------------------------------------------------------
-- 用户表数据
INSERT INTO users (name, phone, email) VALUES('张三', '13800010001', 'zhangsan@park.com'),('李四', '13800010002', 'lisi@park.com'),('王五', '13800010003', 'wangwu@park.com'),('赵六', '13800010004', 'zhaoliu@park.com'),('陈七', '13800010005', 'chenqi@park.com'),('刘八', '13800010006', 'liuba@park.com'),('周九', '13800010007', 'zhoujiu@park.com'),('吴十', '13800010008', 'wushi@park.com'),('郑十一', '13800010009', 'zheng11@park.com'),('孙十二', '13800010010', 'sun12@park.com');-- 车辆表数据
INSERT INTO vehicles (user_id, license_plate, type) VALUES(1, '川A12345', '轿车'),(1, '川A67890', 'SUV'),(2, '赣B12345', '卡车'),(3, '赣C12345', '电动车'),(4, '浙D12345', '摩托车'),(5, '苏E12345', '轿车'),(6, '川F12345', 'SUV'),(7, '湘G12345', '卡车'),(8, '鄂H12345', '电动车'),(9, '鲁J12345', '摩托车'),(10, '晋K12345', '轿车');-- 车位表数据
INSERT INTO parking_spaces (zone, number, status) VALUES('A', '001', 'AVAILABLE'),('A', '002', 'OCCUPIED'),('A', '003', 'MAINTENANCE'),('B', '101', 'AVAILABLE'),('B', '102', 'OCCUPIED'),('C', '201', 'AVAILABLE'),('C', '202', 'OCCUPIED'),('D', '301', 'AVAILABLE'),('D', '302', 'OCCUPIED'),('E', '401', 'MAINTENANCE');INSERT INTO parking_spaces (zone, number, status) VALUES ('F', '402', 'AVAILABLE');-- 收费规则表数据
INSERT INTO fee_rules (vehicle_type, hourly_rate, effective_date) VALUES('轿车', 5.00, '2024-01-01'),('SUV', 7.50, '2024-01-01'),('卡车', 10.00, '2024-01-01'),('电动车', 3.00, '2024-01-01'),('摩托车', 2.50, '2024-01-01'),('轿车', 6.00, '2024-06-01'), -- 费率调整('SUV', 8.50, '2024-06-01'),('卡车', 12.00, '2024-06-01'),('电动车', 3.50, '2024-06-01'),('摩托车', 3.00, '2024-06-01');-- 停车记录表数据
INSERT INTO parking_records (vehicle_id, space_id, entry_time, exit_time, fee) VALUES(1, 2, '2024-03-01 08:00:00', '2024-03-01 10:00:00', 10.00),(2, 4, '2024-03-01 09:00:00', '2024-03-01 12:00:00', 22.50),(3, 5, '2024-03-01 10:00:00', '2024-03-01 15:00:00', 50.00),(4, 6, NOW() - INTERVAL 2 HOUR, NULL, NULL),(5, 7, '2024-03-01 11:00:00', '2024-03-01 13:00:00', 5.00),(6, 8, NOW() - INTERVAL 1 HOUR, NULL, NULL),(7, 9, '2024-03-01 14:00:00', '2024-03-01 17:00:00', 25.50),(8, 10, '2024-03-01 15:00:00', '2024-03-01 18:00:00', 36.00),(9, 1, NOW() - INTERVAL 3 HOUR, NULL, NULL),(10, 3, '2024-03-01 16:00:00', '2024-03-01 19:00:00', 18.00);-- 支付记录表数据
INSERT INTO payment_records (record_id, amount, payment_method, status) VALUES(1, 10.00, '支付宝', 'PAID'),(2, 22.50, '微信', 'PAID'),(3, 50.00, '现金', 'PAID'),(5, 5.00, '信用卡', 'PAID'),(7, 25.50, '支付宝', 'PAID'),(8, 36.00, '微信', 'PAID'),(10, 18.00, '信用卡', 'PAID');-- 3. 查询语句(4个核心查询)
-- --------------------------------------------------------
-- 1. 查询具体用户的所有停车记录(例如:张三)
SELECTu.name AS 车主,v.license_plate AS 车牌,CONCAT(p.zone, '-', p.number) AS 车位,pr.entry_time AS 入场时间,pr.exit_time AS 离场时间,pr.fee AS 费用
FROM users uJOIN vehicles v ON u.user_id = v.user_idJOIN parking_records pr ON v.vehicle_id = pr.vehicle_idJOIN parking_spaces p ON pr.space_id = p.space_id
WHERE u.name = '张三';-- 2. 查询当前正在停车的记录
SELECTv.license_plate AS 车牌,u.name AS 车主姓名,pr.entry_time AS 进入时间,CONCAT(p.zone, '-', p.number) AS 车位位置
FROM parking_records prJOIN vehicles v ON pr.vehicle_id = v.vehicle_idJOIN users u ON v.user_id = u.user_idJOIN parking_spaces p ON pr.space_id = p.space_id
WHERE pr.exit_time IS NULL;-- 3. 已结束停车的费用明细表
SELECTv.type AS 车型,fr.hourly_rate AS 费率,TIMESTAMPDIFF(HOUR, pr.entry_time, pr.exit_time) AS 停车小时,pr.fee AS 总费用,py.payment_method AS 支付方式
FROM parking_records prJOIN vehicles v ON pr.vehicle_id = v.vehicle_idJOIN fee_rules fr ON v.type = fr.vehicle_typeAND pr.entry_time >= fr.effective_dateJOIN payment_records py ON pr.record_id = py.record_id
WHERE pr.exit_time IS NOT NULL;-- 4. 统计车位利用率(每日动态计算)
SELECTCOUNT(*) AS 总车位数,SUM(CASE WHEN status = 'OCCUPIED' THEN 1 ELSE 0 END) AS 已用车位,SUM(CASE WHEN status = 'AVAILABLE' THEN 1 ELSE 0 END) AS 空闲车位,CONCAT(ROUND(SUM(CASE WHEN status = 'OCCUPIED' THEN 1 ELSE 0 END)/COUNT(*)*100,2), '%') AS 利用率,CONCAT(ROUND(SUM(CASE WHEN status = 'AVAILABLE' THEN 1 ELSE 0 END)/COUNT(*)*100,2), '%') AS 空闲率
FROM parking_spaces;
step2: 写路由接口,支持跨域
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import pymysql.cursorsapp = FastAPI()# 添加CORS配置
app.add_middleware(CORSMiddleware,allow_origins=["http://localhost:4200"], # 允许访问的前端域名allow_credentials=True,allow_methods=["*"], # 允许所有HTTP方法allow_headers=["*"], # 允许所有请求头
)
# 数据库连接配置(请根据实际情况修改)
DB_CONFIG = {'host': 'localhost','user': 'root','password': '123456','db': 'school_db', # 修改为实际的数据库名称'charset': 'utf8mb4','cursorclass': pymysql.cursors.DictCursor
}def query_database(query: str, params=None):try:connection = pymysql.connect(**DB_CONFIG)with connection.cursor() as cursor:cursor.execute(query, params)result = cursor.fetchall()connection.close()return resultexcept Exception as e:raise HTTPException(status_code=500, detail=str(e))# 1. 查询具体用户的所有停车记录
@app.get("/user_parking_records/{user_id}")
async def get_user_parking_records(user_id: int):"""获取指定用户的所有停车记录- user_id: 用户ID"""query = """SELECT u.name AS owner,v.license_plate AS license_plate,CONCAT(p.zone, '-', p.number) AS space_location,pr.entry_time AS entry_time,pr.exit_time AS exit_time,pr.fee AS feeFROM users uJOIN vehicles v ON u.user_id = v.user_idJOIN parking_records pr ON v.vehicle_id = pr.vehicle_idJOIN parking_spaces p ON pr.space_id = p.space_idWHERE u.user_id = %s"""data = query_database(query, (user_id,))return {"data": data}# 2. 查询当前正在停车的记录
@app.get("/active_parking_records")
async def get_active_parking_records():"""获取所有正在进行的停车记录"""query = """SELECT v.license_plate AS license_plate,u.name AS owner_name,pr.entry_time AS entry_time,CONCAT(p.zone, '-', p.number) AS space_locationFROM parking_records prJOIN vehicles v ON pr.vehicle_id = v.vehicle_idJOIN users u ON v.user_id = u.user_idJOIN parking_spaces p ON pr.space_id = p.space_idWHERE pr.exit_time IS NULL"""data = query_database(query)return {"data": data}# 3. 查询已结束停车的费用明细
@app.get("/completed_parking_records")
async def get_completed_payments():"""获取所有已完成停车的费用明细"""query = """SELECT v.type AS vehicle_type,fr.hourly_rate AS rate,TIMESTAMPDIFF(HOUR, pr.entry_time, pr.exit_time) AS hours,pr.fee AS total_fee,py.payment_method AS payment_methodFROM parking_records prJOIN vehicles v ON pr.vehicle_id = v.vehicle_idJOIN fee_rules fr ON v.type = fr.vehicle_type AND pr.entry_time >= fr.effective_dateJOIN payment_records py ON pr.record_id = py.record_idWHERE pr.exit_time IS NOT NULL"""data = query_database(query)return {"data": data}# 4. 统计车位利用率
@app.get("/parking_space_utilization")
async def get_parking_space_stats():"""获取车位利用率统计"""query = """SELECT COUNT(*) AS total_spaces,SUM(CASE WHEN status = 'OCCUPIED' THEN 1 ELSE 0 END) AS occupied,SUM(CASE WHEN status = 'AVAILABLE' THEN 1 ELSE 0 END) AS available,ROUND(SUM(CASE WHEN status = 'OCCUPIED' THEN 1 ELSE 0 END)/COUNT(*)*100, 2) AS usage_rate,ROUND(SUM(CASE WHEN status = 'AVAILABLE' THEN 1 ELSE 0 END)/COUNT(*)*100, 2) AS free_rateFROM parking_spaces"""data = query_database(query)return {"data": data[0] if data else {}}if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)
step3:后端+mysql部分弄完了,接下来写angular前端部分,下面是postman测试
查询具体用户的所有停车记录
查询当前正在停车的记录,显示车牌,车主姓名,进入时间,车位位置
查询所有已结束停车的费用明细表,详细列举车型和费率
统计停车场车位的利用率和空闲率http://localhost:8000/user_parking_records/1{"data": [{"owner": "张三","license_plate": "A12345","space_location": "A-002","entry_time": "2024-03-01T08:00:00","exit_time": "2024-03-01T10:00:00","fee": 10.0},{"owner": "张三","license_plate": "A67890","space_location": "B-101","entry_time": "2024-03-01T09:00:00","exit_time": "2024-03-01T12:00:00","fee": 22.5}]
}http://localhost:8000/active_parking_records{"data": [{"license_plate": "粤C12345","owner_name": "王五","entry_time": "2025-03-07T13:15:43","space_location": "C-201"},{"license_plate": "苏E12345","owner_name": "陈七","entry_time": "2025-03-07T14:15:43","space_location": "D-301"},{"license_plate": "鄂H12345","owner_name": "吴十","entry_time": "2025-03-07T12:15:43","space_location": "A-001"}]
}http://localhost:8000/completed_parking_records
{"data": [{"vehicle_type": "轿车","rate": 5.0,"hours": 2,"total_fee": 10.0,"payment_method": "支付宝"},{"vehicle_type": "SUV","rate": 7.5,"hours": 3,"total_fee": 25.5,"payment_method": "支付宝"},{"vehicle_type": "SUV","rate": 7.5,"hours": 3,"total_fee": 22.5,"payment_method": "微信"},{"vehicle_type": "卡车","rate": 10.0,"hours": 3,"total_fee": 36.0,"payment_method": "微信"},{"vehicle_type": "卡车","rate": 10.0,"hours": 5,"total_fee": 50.0,"payment_method": "现金"},{"vehicle_type": "摩托车","rate": 2.5,"hours": 3,"total_fee": 18.0,"payment_method": "信用卡"},{"vehicle_type": "摩托车","rate": 2.5,"hours": 2,"total_fee": 5.0,"payment_method": "信用卡"}]
}http://localhost:8000/parking_space_utilization{"data": {"total_spaces": 11,"occupied": 4,"available": 5,"usage_rate": 36.36,"free_rate": 45.45}
}
step4:C:\Users\Administrator\WebstormProjects\untitled4\src\app\park\parking.service.ts
// parking.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';@Injectable({providedIn: 'root'
})
export class ParkingService {private apiUrl = 'http://localhost:8000';constructor(private http: HttpClient) { }getUserRecords(userId: number): Observable<any> {return this.http.get(`${this.apiUrl}/user_parking_records/${userId}`);}getActiveRecords(): Observable<any> {return this.http.get(`${this.apiUrl}/active_parking_records`);}getCompletedRecords(): Observable<any> {return this.http.get(`${this.apiUrl}/completed_parking_records`);}getParkingStats(): Observable<any> {return this.http.get(`${this.apiUrl}/parking_space_utilization`);}
}
step5:C:\Users\Administrator\WebstormProjects\untitled4\src\app\park\park.component.ts
import { Component ,OnInit} from '@angular/core';
import { ParkingService } from './parking.service';
import {FormsModule} from '@angular/forms';
import {CurrencyPipe, DatePipe, NgForOf, NgIf} from '@angular/common';@Component({selector: 'app-park',imports: [FormsModule,DatePipe,CurrencyPipe,NgForOf,NgIf],templateUrl: './park.component.html',styleUrl: './park.component.css'
})
export class ParkComponent implements OnInit {userRecords: any[] = [];activeRecords: any[] = [];completedRecords: any[] = [];parkingStats: any = {};selectedUserId: number = 1;constructor(private parkingService: ParkingService) {}ngOnInit() {this.loadAllData();}loadAllData() {this.loadUserRecords();this.loadActiveRecords();this.loadCompletedRecords();this.loadParkingStats();}loadUserRecords() {this.parkingService.getUserRecords(this.selectedUserId).subscribe((res: any) => this.userRecords = res.data);}loadActiveRecords() {this.parkingService.getActiveRecords().subscribe((res: any) => this.activeRecords = res.data);}loadCompletedRecords() {this.parkingService.getCompletedRecords().subscribe((res: any) => this.completedRecords = res.data);}loadParkingStats() {this.parkingService.getParkingStats().subscribe((res: any) => this.parkingStats = res.data);}
}
step6:C:\Users\Administrator\WebstormProjects\untitled4\src\app\park\park.component.html
<!-- app.component.html -->
<div class="container"><!-- 用户选择 --><div class="user-select"><label>选择用户ID: </label><input type="number" [(ngModel)]="selectedUserId" (change)="loadUserRecords()" min="1" max="10"></div><!-- 用户停车记录 --><section><h2>用户停车记录 (ID: {{selectedUserId}})</h2><table *ngIf="userRecords.length > 0"><thead><tr><th>车主</th><th>车牌号</th><th>车位位置</th><th>入场时间</th><th>离场时间</th><th>费用</th></tr></thead><tbody><tr *ngFor="let record of userRecords"><td>{{ record.owner }}</td><td>{{ record.license_plate }}</td><td>{{ record.space_location }}</td><td>{{ record.entry_time | date:'medium' }}</td><td>{{ record.exit_time | date:'medium' }}</td><td>{{ record.fee | currency:'CNY':'symbol' }}</td></tr></tbody></table><p *ngIf="userRecords.length === 0">暂无停车记录</p></section><!-- 当前停车记录 --><section><h2>当前停车记录</h2><table *ngIf="activeRecords.length > 0"><thead><tr><th>车牌号</th><th>车主姓名</th><th>进入时间</th><th>车位位置</th></tr></thead><tbody><tr *ngFor="let record of activeRecords"><td>{{ record.license_plate }}</td><td>{{ record.owner_name }}</td><td>{{ record.entry_time | date: 'yyyy-MM-dd HH:mm' }}</td><td>{{ record.space_location }}</td></tr></tbody></table><p *ngIf="activeRecords.length === 0">当前没有车辆停放</p></section><!-- 完成停车费用 --><section><h2>历史费用明细</h2><table *ngIf="completedRecords.length > 0"><thead><tr><th>车型</th><th>费率 (小时)</th><th>停车时长</th><th>总费用</th><th>支付方式</th></tr></thead><tbody><tr *ngFor="let record of completedRecords"><td>{{ record.vehicle_type }}</td><td>{{ record.rate | currency:'CNY':'symbol' }}</td><td>{{ record.hours }} 小时</td><td>{{ record.total_fee | currency:'CNY':'symbol' }}</td><td>{{ record.payment_method }}</td></tr></tbody></table><p *ngIf="completedRecords.length === 0">暂无历史费用记录</p></section><!-- 车位统计 --><section><h2>车位利用率统计</h2><div class="stats-container" *ngIf="parkingStats.total_spaces"><div class="stat-item"><span class="label">总车位数:</span><span class="value">{{ parkingStats.total_spaces }}</span></div><div class="stat-item"><span class="label">已用车位:</span><span class="value">{{ parkingStats.occupied }}</span></div><div class="stat-item"><span class="label">空闲车位:</span><span class="value">{{ parkingStats.available }}</span></div><div class="stat-item highlight"><span class="label">利用率:</span><span class="value">{{ parkingStats.usage_rate }}%</span></div><div class="stat-item highlight"><span class="label">空闲率:</span><span class="value">{{ parkingStats.free_rate }}%</span></div></div><p *ngIf="!parkingStats.total_spaces">加载统计数据中...</p></section>
</div>
step7:C:\Users\Administrator\WebstormProjects\untitled4\src\app\park\park.component.css
/* app.component.css */
.container {max-width: 1200px;margin: 20px auto;padding: 20px;
}section {margin-bottom: 40px;background: #fff;padding: 20px;border-radius: 8px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}h2 {color: #2c3e50;border-bottom: 2px solid #3498db;padding-bottom: 10px;margin-bottom: 20px;
}table {width: 100%;border-collapse: collapse;margin-top: 15px;
}th, td {padding: 12px;text-align: left;border-bottom: 1px solid #ecf0f1;
}th {background-color: #3498db;color: white;
}tr:hover {background-color: #f8f9fa;
}.user-select {margin-bottom: 30px;padding: 15px;background: #f8f9fa;border-radius: 6px;
}.user-select input {padding: 8px;border: 1px solid #ddd;border-radius: 4px;
}.stats-container {display: grid;grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));gap: 20px;margin-top: 20px;
}.stat-item {background: #f8f9fa;padding: 15px;border-radius: 6px;text-align: center;
}.stat-item .label {display: block;color: #7f8c8d;margin-bottom: 5px;
}.stat-item .value {font-size: 1.4em;color: #2c3e50;font-weight: 500;
}.highlight .value {color: #3498db;
}
end