feat: 实现滞留功能

master
wjl 4 months ago
parent 664f247b6e
commit 2746814257

File diff suppressed because it is too large Load Diff

@ -0,0 +1,164 @@
import java.util.*;
// 快递实体类POJO
class ExpressItem {
private String trackingNumber; // 运单号
private String recipient; // 收件人
private String phone; // 联系电话
private String status; // 包裹状态
private Date arrivalTime; // 到站时间
private String pickupCode; // 取件码
public ExpressItem(String trackingNumber, String recipient, String phone) {
this.trackingNumber = trackingNumber;
this.recipient = recipient;
this.phone = phone;
this.status = "待取件"; // 默认状态
this.arrivalTime = new Date();
this.pickupCode = generatePickupCode(); // 自动生成取件码
}
// 生成4位随机取件码0000-9999
private String generatePickupCode() {
Random random = new Random();
return String.format("%04d", random.nextInt(10000));
}
// Getter/Setter 区域省略其他getter/setter
public String getStatus() { return status; }
public String getPickupCode() { return pickupCode; }
// ...其他getter/setter保持原有结构
@Override
public String toString() {
return String.format("""
%s
%s
%s
%s
%tF %tT
""", trackingNumber, recipient, status, pickupCode, arrivalTime, arrivalTime);
}
}
// 快递业务逻辑类
class ExpressService {
private final Map<String, ExpressItem> database = new HashMap<>(); // 内存数据库
private int totalPackages = 0; // 总入库计数器
// 快递入库(返回是否成功)
public boolean addPackage(ExpressItem item) {
if (database.containsKey(item.getTrackingNumber())) return false; // 重复检查
database.put(item.getTrackingNumber(), item);
totalPackages++;
return true;
}
// 标记包裹为已取件
public boolean markAsPickedUp(String trackingNumber) {
ExpressItem item = database.get(trackingNumber);
if (item != null && "待取件".equals(item.getStatus())) { // 双重验证
item.setStatus("已取件");
return true;
}
return false;
}
// 统计方法
public int getTotalPackages() { return totalPackages; }
public int getPendingPackages() {
return (int) database.values().stream() // 使用流式计算
.filter(item -> "待取件".equals(item.getStatus()))
.count();
}
}
// 控制台交互层
public class ExpressManagementSystem {
private static final Scanner scanner = new Scanner(System.in);
private static final ExpressService service = new ExpressService();
public static void main(String[] args) {
while (true) {
printMenu();
int choice = getIntInput("请选择操作:");
switch (choice) {
case 1 -> addPackage();
case 2 -> pickupPackage();
case 3 -> searchPackage();
case 4 -> showStatistics();
case 5 -> System.exit(0);
default -> System.out.println("无效输入!");
}
}
}
private static void printMenu() {
System.out.println("""
===== =====
1.
2.
3.
4.
5. 退
""");
}
private static void addPackage() {
System.out.println("\n--- 快递入库 ---");
String trackingNumber = getNonEmptyInput("请输入运单号:");
String recipient = getNonEmptyInput("请输入收件人姓名:");
String phone = getValidPhone();
ExpressItem newItem = new ExpressItem(trackingNumber, recipient, phone);
System.out.println(service.addPackage(newItem)
? "入库成功!\n取件码" + newItem.getPickupCode()
: "该运单号已存在!");
}
private static void pickupPackage() {
System.out.println("\n--- 快递出库 ---");
String trackingNumber = getNonEmptyInput("请输入运单号:");
String inputCode = getNonEmptyInput("请输入取件码:");
ExpressItem item = service.getPackage(trackingNumber);
if (item == null) {
System.out.println("运单号不存在!");
return;
}
System.out.println(inputCode.equals(item.getPickupCode())
? service.markAsPickedUp(trackingNumber)
? "取件成功!"
: "该包裹已取件!"
: "取件码错误!");
}
// 辅助方法区域
private static String getNonEmptyInput(String prompt) {
while (true) {
String input = scanner.nextLine().trim();
if (!input.isEmpty()) return input;
System.out.print("输入不能为空,请重新输入:");
}
}
private static String getValidPhone() {
while (true) {
String phone = scanner.nextLine().trim();
if (phone.matches("1[3-9]\\d{9}")) return phone; // 中国手机号正则
System.out.print("手机号格式不正确,请重新输入:");
}
}
private static int getIntInput(String prompt) {
while (true) {
try {
return Integer.parseInt(scanner.nextLine().trim());
} catch (NumberFormatException e) {
System.out.print("请输入有效数字:");
}
}
}
}

@ -0,0 +1,261 @@
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
import com.google.gson.Gson; // 需要gson库支持添加依赖
/**
*
*
*/
class ExpressIteml {
// ... [原有属性保持不变]
private double weight; // 包裹重量(kg)
private Dimension dimension; // 包裹尺寸(cm)
private int storageDays; // 存放天数
public ExpressItem(String trackingNumber, String recipient, String phone,
double weight, int length, int width, int height) {
// ... [原有初始化代码]
this.weight = weight;
this.dimension = new Dimension(length, width, height);
this.storageDays = 0;
}
// 新增内部类:包裹尺寸
static class Dimension {
int length;
int width;
int height;
Dimension(int l, int w, int h) {
this.length = l;
this.width = w;
this.height = h;
}
@Override
public String toString() {
return String.format("%dx%dx%d cm", length, width, height);
}
}
// 新增业务方法:计算体积重量(航空件常用算法)
public double calculateVolumetricWeight() {
// 体积重量(kg) = 长(cm)*宽(cm)*高(cm)/6000
return (dimension.length * dimension.width * dimension.height) / 6000.0;
}
// Getter/Setter 区域(新增属性)
public double getWeight() { return weight; }
public Dimension getDimension() { return dimension; }
public int getStorageDays() { return storageDays; }
public void setStorageDays(int days) { this.storageDays = days; }
}
/**
*
*
*/
class ExpressService {
// ... [原有属性保持不变]
private final DataPersistence persistence = new DataPersistence();
private static final int MAX_STORAGE_DAYS = 7; // 最大免费存放天数
public ExpressService() {
// 启动时加载持久化数据
database = persistence.loadData();
// 更新所有包裹的存放天数(模拟每日自动更新)
updateStorageDays();
}
// 新增方法:更新所有包裹的存放天数
private void updateStorageDays() {
long today = new Date().getTime() / (24 * 3600 * 1000);
database.values().forEach(item -> {
long arrivalDay = item.getArrivalTime().getTime() / (24 * 3600 * 1000);
item.setStorageDays((int) (today - arrivalDay));
});
}
// 新增方法:获取超时包裹列表
public List<ExpressItem> getOverduePackages() {
return database.values().stream()
.filter(item -> item.getStorageDays() > MAX_STORAGE_DAYS)
.collect(Collectors.toList());
}
// 新增方法:按体积重量排序查询
public List<ExpressItem> searchByVolumetricWeight(double min, double max) {
return database.values().stream()
.filter(item -> {
double volWeight = item.calculateVolumetricWeight();
return volWeight >= min && volWeight <= max;
})
.sorted(Comparator.comparingDouble(ExpressItem::calculateVolumetricWeight).reversed())
.collect(Collectors.toList());
}
// 新增方法:批量标记为已取件
public int batchMarkAsPickedUp(List<String> trackingNumbers) {
return (int) trackingNumbers.stream()
.filter(tn -> {
ExpressItem item = database.get(tn);
return item != null && "待取件".equals(item.getStatus());
})
.peek(tn -> database.get(tn).setStatus("已取件"))
.count();
}
// 新增方法:获取仓库使用率统计
public Map<String, Double> getStorageUsage() {
Map<String, Double> usage = new HashMap<>();
long totalVolume = database.values().stream()
.mapToLong(item ->
item.getDimension().length *
item.getDimension().width *
item.getDimension().height
).sum();
usage.put("totalVolume", (double) totalVolume);
usage.put("averageVolume", totalVolume / (double) database.size());
return usage;
}
// 新增方法:数据持久化
public void saveData() {
persistence.saveData(database);
}
}
/**
*
* 使JSON
*/
class DataPersistence {
private static final String DATA_FILE = "express_data.json";
private final Gson gson = new Gson();
public Map<String, ExpressItem> loadData() {
try (Reader reader = new FileReader(DATA_FILE)) {
Type type = new com.google.gson.reflect.TypeToken<Map<String, ExpressItem>>() {}.getType();
return gson.fromJson(reader, type);
} catch (FileNotFoundException e) {
return new HashMap<>(); // 首次运行无数据文件
} catch (Exception e) {
System.err.println("数据加载错误: " + e.getMessage());
return new HashMap<>();
}
}
public void saveData(Map<String, ExpressItem> data) {
try (Writer writer = new FileWriter(DATA_FILE)) {
gson.toJson(data, writer);
} catch (Exception e) {
System.err.println("数据保存错误: " + e.getMessage());
}
}
}
/**
*
*
*/
public class ExpressManagementSystem {
// ... [原有属性保持不变]
private static final ExpressService service = new ExpressService();
private static void printAdvancedMenu() {
System.out.println("""
===== =====
6.
7.
8.
9. 使
10. 退
""");
}
private static void batchPickup() {
System.out.println("\n--- 批量取件 ---");
List<String> trackingNumbers = new ArrayList<>();
while (true) {
String tn = getNonEmptyInput("输入运单号输入0结束");
if ("0".equals(tn)) break;
trackingNumbers.add(tn);
}
int successCount = service.batchMarkAsPickedUp(trackingNumbers);
System.out.printf("成功取件%d个包裹\n", successCount);
}
private static void showOverduePackages() {
System.out.println("\n--- 超时包裹列表 ---");
List<ExpressItem> overdue = service.getOverduePackages();
if (overdue.isEmpty()) {
System.out.println("当前没有超时包裹");
return;
}
overdue.forEach(item ->
System.out.printf("运单号:%s 超时%d天\n",
item.getTrackingNumber(),
item.getStorageDays() - ExpressService.MAX_STORAGE_DAYS));
}
private static void volumetricSearch() {
System.out.println("\n--- 体积重量查询 ---");
double min = getDoubleInput("最小体积重量(kg)");
double max = getDoubleInput("最大体积重量(kg)");
List<ExpressItem> results = service.searchByVolumetricWeight(min, max);
if (results.isEmpty()) {
System.out.println("未找到匹配包裹");
return;
}
results.forEach(item ->
System.out.printf("运单号:%s 体积重量:%.2fkg\n",
item.getTrackingNumber(),
item.calculateVolumetricWeight()));
}
private static void showStorageStats() {
System.out.println("\n--- 仓库使用统计 ---");
Map<String, Double> stats = service.getStorageUsage();
System.out.printf("总占用体积:%,d cm³\n", stats.get("totalVolume").intValue());
System.out.printf("平均单件体积:%,.1f cm³\n", stats.get("averageVolume"));
}
public static void main(String[] args) {
while (true) {
printMenu();
int choice = getIntInput("请选择操作:");
switch (choice) {
case 1 -> addPackage();
case 2 -> pickupPackage();
case 3 -> searchPackage();
case 4 -> showStatistics();
case 5 -> printAdvancedMenu();
case 6 -> batchPickup();
case 7 -> showOverduePackages();
case 8 -> volumetricSearch();
case 9 -> showStorageStats();
case 10 -> {
service.saveData();
System.exit(0);
}
default -> System.out.println("无效输入!");
}
}
}
// 新增方法获取double类型输入
private static double getDoubleInput(String prompt) {
while (true) {
try {
return Double.parseDouble(scanner.nextLine().trim());
} catch (NumberFormatException e) {
System.out.print("请输入有效数字:");
}
}
}
}

@ -0,0 +1,263 @@
import java.security.*;
import java.util.*;
import java.util.regex.*;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
/**
*
* /
*/
class UserManagementService {
// 内存存储结构(实际项目应使用数据库)
private final Map<String, User> userDatabase = new HashMap<>();
private final Map<String, Session> activeSessions = new HashMap<>();
private static final String SECRET_KEY = "express@Sys#Key2025"; // 加密密钥(生产环境应存储在安全配置)
/**
*
* @param username 4-16
* @param password
* @param role admin/staff/customer
* @return
*/
public RegistrationResult registerUser(String username, String password, String role)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// 1. 参数验证
if (!isValidUsername(username)) {
return new RegistrationResult(false, "用户名需为4-16位字母数字");
}
if (userDatabase.containsKey(username)) {
return new RegistrationResult(false, "用户名已存在");
}
if (!isValidPassword(password)) {
return new RegistrationResult(false, "密码需包含大小写字母和数字至少8位");
}
if (!isValidRole(role)) {
return new RegistrationResult(false, "无效角色类型");
}
// 2. 密码加密处理
String salt = generateSalt();
String hashedPwd = hashPassword(password, salt);
// 3. 创建用户对象
User newUser = new User(username, hashedPwd, salt, role);
userDatabase.put(username, newUser);
return new RegistrationResult(true, "注册成功", newUser);
}
/**
*
* @param username
* @param password
* @return
*/
public AuthenticationResult authenticate(String username, String password)
throws NoSuchAlgorithmException, InvalidKeySpecException {
User user = userDatabase.get(username);
if (user == null) {
return new AuthenticationResult(false, "用户不存在");
}
// 验证密码
String testHash = hashPassword(password, user.getSalt());
if (!testHash.equals(user.getHashedPassword())) {
return new AuthenticationResult(false, "密码错误");
}
// 生成会话令牌JWT风格
String token = generateSessionToken(username);
activeSessions.put(token, new Session(token, username, new Date()));
return new AuthenticationResult(true, "认证成功", token);
}
/**
*
* @param password
* @return
*/
private boolean isValidPassword(String password) {
if (password.length() < 8) return false;
boolean hasUpper = false, hasLower = false, hasDigit = false;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) hasUpper = true;
else if (Character.isLowerCase(c)) hasLower = true;
else if (Character.isDigit(c)) hasDigit = true;
}
return hasUpper && hasLower && hasDigit;
}
/**
* 16
*/
private String generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
/**
* PBKDF2
*/
private String hashPassword(String password, String salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
int iterations = 10000;
int keyLength = 256;
char[] passwordChars = password.toCharArray();
byte[] saltBytes = Base64.getDecoder().decode(salt);
PBEKeySpec spec = new PBEKeySpec(passwordChars, saltBytes, iterations, keyLength);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = skf.generateSecret(spec).getEncoded();
return String.format("%d$%s$%s",
iterations,
Base64.getEncoder().encodeToString(saltBytes),
Base64.getEncoder().encodeToString(hash));
}
/**
* HMAC-SHA256
*/
private String generateSessionToken(String username) {
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
String baseString = username + new Date().getTime();
return Base64.getEncoder().encodeToString(
sha256_HMAC.doFinal(baseString.getBytes())
);
} catch (Exception e) {
throw new RuntimeException("令牌生成失败", e);
}
}
/**
*
* @param requiredRole
*/
public static class RoleValidator {
private final String currentRole;
public RoleValidator(String sessionToken) {
// 实际应从会话中获取用户角色
this.currentRole = "customer"; // 示例值
}
public void validate(String requiredRole) throws InsufficientPermissionsException {
if (!currentRole.equals(requiredRole)) {
throw new InsufficientPermissionsException(
String.format("需要%s权限当前角色%s", requiredRole, currentRole));
}
}
}
/**
*
*/
static class User {
private final String username;
private final String hashedPassword;
private final String salt;
private final String role;
User(String username, String hashedPassword, String salt, String role) {
this.username = username;
this.hashedPassword = hashedPassword;
this.salt = salt;
this.role = role;
}
// Getter方法根据安全需求限制访问
public String getUsername() { return username; }
public String getHashedPassword() { return hashedPassword; }
public String getSalt() { return salt; }
public String getRole() { return role; }
}
/**
*
*/
static class Session {
private final String token;
private final String username;
private final Date createdAt;
Session(String token, String username, Date createdAt) {
this.token = token;
this.username = username;
this.createdAt = createdAt;
}
// Getter方法
public String getToken() { return token; }
public String getUsername() { return username; }
public Date getCreatedAt() { return createdAt; }
}
/**
*
*/
static class RegistrationResult {
private final boolean success;
private final String message;
private final User user;
RegistrationResult(boolean success, String message) {
this(success, message, null);
}
RegistrationResult(boolean success, String message, User user) {
this.success = success;
this.message = message;
this.user = user;
}
// Getter方法
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
public User getUser() { return user; }
}
/**
*
*/
static class AuthenticationResult {
private final boolean success;
private final String message;
private final String token;
AuthenticationResult(boolean success, String message) {
this(success, message, null);
}
AuthenticationResult(boolean success, String message, String token) {
this.success = success;
this.message = message;
this.token = token;
}
// Getter方法
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
public String getToken() { return token; }
}
/**
*
*/
static class InsufficientPermissionsException extends SecurityException {
public InsufficientPermissionsException(String message) {
super(message);
}
}
}

@ -0,0 +1,31 @@
package com.model.enums;
import java.io.Serializable;
import com.baomidou.mybatisplus.enums.IEnum;
/**
* IEnum spring-mybatis.xml typeEnumsPackage
*/
public enum TypeEnum implements IEnum {
DISABLED(0, "禁用"),
NORMAL(1, "正常");
private final int value;
private final String desc;
TypeEnum(final int value, final String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Serializable getValue() {
return this.value;
}
// Jackson 注解为 JsonValue 返回中文 json 描述
public String getDesc() {
return this.desc;
}
}
Loading…
Cancel
Save