@ -1,84 +0,0 @@
|
||||
package com.startsmake.llrisetabbardemo.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ImageAdapter extends BaseAdapter {
|
||||
|
||||
private Context context;
|
||||
private List<Uri> imageUris;
|
||||
private static final int MAX_IMAGES = 9;
|
||||
|
||||
public ImageAdapter(Context context, List<Uri> imageUris) {
|
||||
this.context = context;
|
||||
this.imageUris = imageUris;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return Math.min(imageUris.size() + 1, MAX_IMAGES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
if (position < imageUris.size()) {
|
||||
return imageUris.get(position);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.item_image, parent, false);
|
||||
holder = new ViewHolder();
|
||||
holder.imageView = convertView.findViewById(R.id.imageView);
|
||||
holder.deleteButton = convertView.findViewById(R.id.btnDelete);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
if (position < imageUris.size()) {
|
||||
// 显示已选择的图片
|
||||
Uri imageUri = imageUris.get(position);
|
||||
Glide.with(context)
|
||||
.load(imageUri)
|
||||
.placeholder(android.R.drawable.ic_menu_gallery) // 使用系统图标作为占位符
|
||||
.into(holder.imageView);
|
||||
|
||||
holder.deleteButton.setVisibility(View.VISIBLE);
|
||||
holder.deleteButton.setOnClickListener(v -> {
|
||||
imageUris.remove(position);
|
||||
notifyDataSetChanged();
|
||||
});
|
||||
} else {
|
||||
// 显示添加按钮
|
||||
holder.imageView.setImageResource(android.R.drawable.ic_input_add); // 使用系统图标
|
||||
holder.deleteButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
ImageView imageView;
|
||||
ImageView deleteButton;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.startsmake.llrisetabbardemo.api;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ApiClient {
|
||||
private static final String BASE_URL = "http://localhost:8080/";
|
||||
private static Retrofit retrofit = null;
|
||||
|
||||
public static Retrofit getClient() {
|
||||
if (retrofit == null) {
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
retrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.client(client)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build();
|
||||
}
|
||||
return retrofit;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.startsmake.llrisetabbardemo.api;
|
||||
|
||||
import com.startsmake.llrisetabbardemo.api.response.BaseResponse;
|
||||
import com.startsmake.llrisetabbardemo.api.response.ProductResponse;
|
||||
import com.startsmake.llrisetabbardemo.api.response.UserResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Field;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface ApiService {
|
||||
// 获取API状态
|
||||
@GET("api")
|
||||
Call<BaseResponse> getApiStatus();
|
||||
|
||||
// 用户登录
|
||||
@FormUrlEncoded
|
||||
@POST("api/login")
|
||||
Call<UserResponse> login(@Field("phone") String phone, @Field("password") String password);
|
||||
|
||||
// 用户注册
|
||||
@FormUrlEncoded
|
||||
@POST("api/register")
|
||||
Call<UserResponse> register(@Field("phone") String phone, @Field("password") String password,
|
||||
@Field("username") String username);
|
||||
|
||||
// 获取商品列表
|
||||
@GET("api/products")
|
||||
Call<BaseResponse<List<ProductResponse>>> getProducts();
|
||||
|
||||
// 搜索商品
|
||||
@GET("api/products/search")
|
||||
Call<BaseResponse<List<ProductResponse>>> searchProducts(@Query("keyword") String keyword);
|
||||
|
||||
// 获取商品详情
|
||||
@GET("api/products/detail")
|
||||
Call<BaseResponse<ProductResponse>> getProductDetail(@Query("id") String productId);
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.startsmake.llrisetabbardemo.api.response;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class BaseResponse<T> {
|
||||
@SerializedName("status")
|
||||
private String status;
|
||||
|
||||
@SerializedName("message")
|
||||
private String message;
|
||||
|
||||
@SerializedName("data")
|
||||
private T data;
|
||||
|
||||
// Getters and Setters
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return "success".equals(status);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package com.startsmake.llrisetabbardemo.api.response;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ProductResponse {
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
@SerializedName("title")
|
||||
private String title;
|
||||
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
|
||||
@SerializedName("category")
|
||||
private String category;
|
||||
|
||||
@SerializedName("price")
|
||||
private double price;
|
||||
|
||||
@SerializedName("image_urls")
|
||||
private List<String> imageUrls;
|
||||
|
||||
@SerializedName("contact")
|
||||
private String contact;
|
||||
|
||||
@SerializedName("publish_time")
|
||||
private long publishTime;
|
||||
|
||||
@SerializedName("seller_id")
|
||||
private String sellerId;
|
||||
|
||||
// Getters and Setters
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public List<String> getImageUrls() {
|
||||
return imageUrls;
|
||||
}
|
||||
|
||||
public void setImageUrls(List<String> imageUrls) {
|
||||
this.imageUrls = imageUrls;
|
||||
}
|
||||
|
||||
public String getContact() {
|
||||
return contact;
|
||||
}
|
||||
|
||||
public void setContact(String contact) {
|
||||
this.contact = contact;
|
||||
}
|
||||
|
||||
public long getPublishTime() {
|
||||
return publishTime;
|
||||
}
|
||||
|
||||
public void setPublishTime(long publishTime) {
|
||||
this.publishTime = publishTime;
|
||||
}
|
||||
|
||||
public String getSellerId() {
|
||||
return sellerId;
|
||||
}
|
||||
|
||||
public void setSellerId(String sellerId) {
|
||||
this.sellerId = sellerId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.startsmake.llrisetabbardemo.api.response;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class UserResponse extends BaseResponse<UserResponse.UserInfo> {
|
||||
public static class UserInfo {
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
@SerializedName("username")
|
||||
private String username;
|
||||
|
||||
@SerializedName("phone")
|
||||
private String phone;
|
||||
|
||||
@SerializedName("token")
|
||||
private String token;
|
||||
|
||||
// Getters and Setters
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
'vac' 不是内部或外部命令,也不是可运行的程序
|
||||
或批处理文件。
|
||||
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>secondhand-market</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>SecondHandMarket</name>
|
||||
<description>二手市场后端应用</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.15</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot 核心依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Data JPA -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL 驱动 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- H2 内存数据库 (用于开发测试) -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot 测试 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security 测试 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok (简化实体类编写) -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 用于密码加密的 BCrypt -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>central</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</project>
|
||||
@ -0,0 +1,13 @@
|
||||
package com.example.secondhandmarket;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SecondHandMarketApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SecondHandMarketApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package com.example.secondhandmarket.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
CorsConfiguration corsConfig = new CorsConfiguration();
|
||||
|
||||
// 允许所有来源
|
||||
corsConfig.setAllowedOrigins(Arrays.asList("*"));
|
||||
|
||||
// 允许所有HTTP方法
|
||||
corsConfig.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||
|
||||
// 允许的请求头
|
||||
corsConfig.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With"));
|
||||
|
||||
// 允许暴露的响应头
|
||||
corsConfig.setExposedHeaders(Arrays.asList("Content-Length", "Content-Type", "X-Total-Count"));
|
||||
|
||||
// 允许携带认证信息
|
||||
corsConfig.setAllowCredentials(true);
|
||||
|
||||
// 预检请求的缓存时间
|
||||
corsConfig.setMaxAge(3600L);
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", corsConfig);
|
||||
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.example.secondhandmarket.config;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
|
||||
// 处理一般运行时异常
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public ResponseEntity<?> handleRuntimeException(RuntimeException ex, WebRequest request) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("error", ex.getMessage());
|
||||
body.put("status", HttpStatus.BAD_REQUEST.value());
|
||||
|
||||
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// 处理空指针异常
|
||||
@ExceptionHandler(NullPointerException.class)
|
||||
public ResponseEntity<?> handleNullPointerException(NullPointerException ex, WebRequest request) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("error", "请求处理时发生错误,请稍后再试");
|
||||
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
|
||||
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
// 处理异常的通用方法
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<?> handleGenericException(Exception ex, WebRequest request) {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("error", "服务器内部错误");
|
||||
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
|
||||
// 记录异常日志
|
||||
ex.printStackTrace();
|
||||
|
||||
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package com.example.secondhandmarket.controller;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.ChatMessage;
|
||||
import com.example.secondhandmarket.service.ChatMessageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/messages")
|
||||
public class ChatMessageController {
|
||||
|
||||
@Autowired
|
||||
private ChatMessageService chatMessageService;
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> sendMessage(@RequestBody ChatMessage message) {
|
||||
try {
|
||||
ChatMessage sentMessage = chatMessageService.sendMessage(message);
|
||||
return ResponseEntity.ok(sentMessage);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/conversation/{userId1}/{userId2}")
|
||||
public ResponseEntity<?> getConversation(
|
||||
@PathVariable Long userId1,
|
||||
@PathVariable Long userId2) {
|
||||
List<ChatMessage> messages = chatMessageService.getConversationBetweenUsers(userId1, userId2);
|
||||
// 标记消息为已读
|
||||
chatMessageService.markAllAsRead(userId2, userId1);
|
||||
return ResponseEntity.ok(messages);
|
||||
}
|
||||
|
||||
@GetMapping("/unread/{userId}")
|
||||
public ResponseEntity<?> getUnreadMessages(@PathVariable Long userId) {
|
||||
List<ChatMessage> unreadMessages = chatMessageService.getUnreadMessagesByUserId(userId);
|
||||
return ResponseEntity.ok(unreadMessages);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/read")
|
||||
public ResponseEntity<?> markAsRead(@PathVariable Long id) {
|
||||
try {
|
||||
chatMessageService.markAsRead(id);
|
||||
return ResponseEntity.ok(Map.of("message", "消息已标记为已读"));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/read-all/{userId}/{senderId}")
|
||||
public ResponseEntity<?> markAllAsRead(
|
||||
@PathVariable Long userId,
|
||||
@PathVariable Long senderId) {
|
||||
try {
|
||||
chatMessageService.markAllAsRead(userId, senderId);
|
||||
return ResponseEntity.ok(Map.of("message", "所有消息已标记为已读"));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/conversations/{userId}")
|
||||
public ResponseEntity<?> getUserConversations(@PathVariable Long userId) {
|
||||
List<Long> conversationUserIds = chatMessageService.getUserConversations(userId);
|
||||
return ResponseEntity.ok(conversationUserIds);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,132 @@
|
||||
package com.example.secondhandmarket.controller;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.Item;
|
||||
import com.example.secondhandmarket.service.ItemService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/items")
|
||||
public class ItemController {
|
||||
|
||||
@Autowired
|
||||
private ItemService itemService;
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> createItem(@RequestBody Item item) {
|
||||
try {
|
||||
Item createdItem = itemService.createItem(item);
|
||||
return ResponseEntity.ok(createdItem);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<?> getItemById(@PathVariable Long id) {
|
||||
// 增加浏览次数
|
||||
itemService.incrementViewCount(id);
|
||||
|
||||
return itemService.findById(id)
|
||||
.map(item -> ResponseEntity.ok(item))
|
||||
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<?> getAllItems(
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
Pageable pageable = PageRequest.of(page, size);
|
||||
Page<Item> items = itemService.findAllAvailable(pageable);
|
||||
return ResponseEntity.ok(items);
|
||||
}
|
||||
|
||||
@GetMapping("/category/{category}")
|
||||
public ResponseEntity<?> getItemsByCategory(
|
||||
@PathVariable String category,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
Pageable pageable = PageRequest.of(page, size);
|
||||
Page<Item> items = itemService.findByCategory(category, pageable);
|
||||
return ResponseEntity.ok(items);
|
||||
}
|
||||
|
||||
@GetMapping("/seller/{sellerId}")
|
||||
public ResponseEntity<?> getItemsBySeller(
|
||||
@PathVariable Long sellerId,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
Pageable pageable = PageRequest.of(page, size);
|
||||
Page<Item> items = itemService.findBySeller(sellerId, pageable);
|
||||
return ResponseEntity.ok(items);
|
||||
}
|
||||
|
||||
@GetMapping("/search")
|
||||
public ResponseEntity<?> searchItems(
|
||||
@RequestParam String keyword,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
Pageable pageable = PageRequest.of(page, size);
|
||||
Page<Item> items = itemService.searchItems(keyword, pageable);
|
||||
return ResponseEntity.ok(items);
|
||||
}
|
||||
|
||||
@GetMapping("/price-range")
|
||||
public ResponseEntity<?> getItemsByPriceRange(
|
||||
@RequestParam Double minPrice,
|
||||
@RequestParam Double maxPrice,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
Pageable pageable = PageRequest.of(page, size);
|
||||
Page<Item> items = itemService.findByPriceRange(minPrice, maxPrice, pageable);
|
||||
return ResponseEntity.ok(items);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<?> updateItem(@PathVariable Long id, @RequestBody Item item) {
|
||||
try {
|
||||
item.setId(id);
|
||||
Item updatedItem = itemService.updateItem(item);
|
||||
return ResponseEntity.ok(updatedItem);
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<?> deleteItem(@PathVariable Long id) {
|
||||
try {
|
||||
itemService.deleteItem(id);
|
||||
return ResponseEntity.ok(Map.of("message", "商品删除成功"));
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/status")
|
||||
public ResponseEntity<?> changeItemStatus(@PathVariable Long id, @RequestBody Map<String, String> statusMap) {
|
||||
try {
|
||||
String status = statusMap.get("status");
|
||||
itemService.changeStatus(id, status);
|
||||
return ResponseEntity.ok(Map.of("message", "状态更新成功"));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/like")
|
||||
public ResponseEntity<?> likeItem(@PathVariable Long id) {
|
||||
try {
|
||||
itemService.incrementLikeCount(id);
|
||||
return ResponseEntity.ok(Map.of("message", "点赞成功"));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.example.secondhandmarket.controller;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.User;
|
||||
import com.example.secondhandmarket.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/users")
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<?> register(@RequestBody User user) {
|
||||
try {
|
||||
User registeredUser = userService.register(user);
|
||||
return ResponseEntity.ok(registeredUser);
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<?> login(@RequestBody Map<String, String> credentials) {
|
||||
String phone = credentials.get("phone");
|
||||
String password = credentials.get("password");
|
||||
|
||||
return userService.login(phone, password)
|
||||
.map(user -> ResponseEntity.ok(user))
|
||||
.orElseGet(() -> ResponseEntity.badRequest().body(Map.of("error", "手机号或密码错误")));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<?> getUserById(@PathVariable Long id) {
|
||||
return userService.findById(id)
|
||||
.map(user -> ResponseEntity.ok(user))
|
||||
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<?> updateUser(@PathVariable Long id, @RequestBody User user) {
|
||||
try {
|
||||
user.setId(id);
|
||||
User updatedUser = userService.update(user);
|
||||
return ResponseEntity.ok(updatedUser);
|
||||
} catch (RuntimeException e) {
|
||||
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/exists/phone/{phone}")
|
||||
public ResponseEntity<?> checkPhoneExists(@PathVariable String phone) {
|
||||
return ResponseEntity.ok(Map.of("exists", userService.existsByPhone(phone)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.example.secondhandmarket.model.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity
|
||||
@Table(name = "chat_messages")
|
||||
@Data
|
||||
public class ChatMessage {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "sender_id", nullable = false)
|
||||
private User sender;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "receiver_id", nullable = false)
|
||||
private User receiver;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "item_id")
|
||||
private Item item;
|
||||
|
||||
@Column(nullable = false, columnDefinition = "TEXT")
|
||||
private String content;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Date time;
|
||||
|
||||
private boolean read = false;
|
||||
private String type = "text";
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
package com.example.secondhandmarket.model.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import javax.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "items")
|
||||
@Data
|
||||
public class Item {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String title;
|
||||
|
||||
private String description;
|
||||
|
||||
@Column(nullable = false)
|
||||
private BigDecimal price;
|
||||
|
||||
private String category;
|
||||
private String subcategory;
|
||||
|
||||
private String condition;
|
||||
private String status = "available";
|
||||
|
||||
@ElementCollection
|
||||
private List<String> images;
|
||||
|
||||
@Column(name = "view_count")
|
||||
private int viewCount = 0;
|
||||
|
||||
@Column(name = "like_count")
|
||||
private int likeCount = 0;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private Date createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private Date updatedAt;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "seller_id", nullable = false)
|
||||
private User seller;
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.example.secondhandmarket.model.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
@Data
|
||||
public class User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private String phone;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String password;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String username;
|
||||
|
||||
private String avatar;
|
||||
private String bio;
|
||||
|
||||
@Column(name = "register_time")
|
||||
private Date registerTime;
|
||||
|
||||
@Column(name = "last_login_time")
|
||||
private Date lastLoginTime;
|
||||
|
||||
private boolean enabled = true;
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.example.secondhandmarket.repository;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.ChatMessage;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long> {
|
||||
|
||||
@Query("SELECT m FROM ChatMessage m WHERE (m.sender.id = :userId1 AND m.receiver.id = :userId2) OR (m.sender.id = :userId2 AND m.receiver.id = :userId1) ORDER BY m.time ASC")
|
||||
List<ChatMessage> findConversationBetweenUsers(@Param("userId1") Long userId1, @Param("userId2") Long userId2);
|
||||
|
||||
@Query("SELECT m FROM ChatMessage m WHERE m.receiver.id = :userId AND m.read = false ORDER BY m.time DESC")
|
||||
List<ChatMessage> findUnreadMessagesByUserId(@Param("userId") Long userId);
|
||||
|
||||
List<ChatMessage> findByItemIdAndSenderIdAndReceiverId(Long itemId, Long senderId, Long receiverId);
|
||||
|
||||
@Query("SELECT DISTINCT CASE WHEN m.sender.id = :userId THEN m.receiver.id ELSE m.sender.id END FROM ChatMessage m WHERE m.sender.id = :userId OR m.receiver.id = :userId")
|
||||
List<Long> findUserConversations(@Param("userId") Long userId);
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.example.secondhandmarket.repository;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.Item;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ItemRepository extends JpaRepository<Item, Long> {
|
||||
Page<Item> findByStatus(String status, Pageable pageable);
|
||||
Page<Item> findByCategoryAndStatus(String category, String status, Pageable pageable);
|
||||
Page<Item> findBySellerIdAndStatus(Long sellerId, String status, Pageable pageable);
|
||||
|
||||
@Query("SELECT i FROM Item i WHERE i.status = 'available' AND (i.title LIKE %:keyword% OR i.description LIKE %:keyword%)")
|
||||
Page<Item> searchItems(@Param("keyword") String keyword, Pageable pageable);
|
||||
|
||||
@Query("SELECT i FROM Item i WHERE i.status = 'available' AND i.price BETWEEN :minPrice AND :maxPrice")
|
||||
Page<Item> findByPriceRange(@Param("minPrice") Double minPrice, @Param("maxPrice") Double maxPrice, Pageable pageable);
|
||||
|
||||
void incrementViewCount(Long id);
|
||||
void incrementLikeCount(Long id);
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.example.secondhandmarket.repository;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByPhone(String phone);
|
||||
boolean existsByPhone(String phone);
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.example.secondhandmarket.service;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.ChatMessage;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ChatMessageService {
|
||||
ChatMessage sendMessage(ChatMessage message);
|
||||
List<ChatMessage> getConversationBetweenUsers(Long userId1, Long userId2);
|
||||
List<ChatMessage> getUnreadMessagesByUserId(Long userId);
|
||||
void markAsRead(Long messageId);
|
||||
void markAllAsRead(Long userId, Long senderId);
|
||||
List<Long> getUserConversations(Long userId);
|
||||
Optional<ChatMessage> findById(Long id);
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.example.secondhandmarket.service;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.Item;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ItemService {
|
||||
Item createItem(Item item);
|
||||
Optional<Item> findById(Long id);
|
||||
Page<Item> findAllAvailable(Pageable pageable);
|
||||
Page<Item> findByCategory(String category, Pageable pageable);
|
||||
Page<Item> findBySeller(Long sellerId, Pageable pageable);
|
||||
Page<Item> searchItems(String keyword, Pageable pageable);
|
||||
Page<Item> findByPriceRange(Double minPrice, Double maxPrice, Pageable pageable);
|
||||
Item updateItem(Item item);
|
||||
void deleteItem(Long id);
|
||||
void changeStatus(Long id, String status);
|
||||
void incrementViewCount(Long id);
|
||||
void incrementLikeCount(Long id);
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.example.secondhandmarket.service;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.User;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserService {
|
||||
User register(User user);
|
||||
Optional<User> login(String phone, String password);
|
||||
Optional<User> findById(Long id);
|
||||
Optional<User> findByPhone(String phone);
|
||||
User update(User user);
|
||||
boolean existsByPhone(String phone);
|
||||
void updateLastLoginTime(Long userId);
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package com.example.secondhandmarket.service.impl;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.ChatMessage;
|
||||
import com.example.secondhandmarket.repository.ChatMessageRepository;
|
||||
import com.example.secondhandmarket.service.ChatMessageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class ChatMessageServiceImpl implements ChatMessageService {
|
||||
|
||||
@Autowired
|
||||
private ChatMessageRepository chatMessageRepository;
|
||||
|
||||
@Override
|
||||
public ChatMessage sendMessage(ChatMessage message) {
|
||||
message.setTime(new Date());
|
||||
message.setRead(false);
|
||||
return chatMessageRepository.save(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChatMessage> getConversationBetweenUsers(Long userId1, Long userId2) {
|
||||
return chatMessageRepository.findConversationBetweenUsers(userId1, userId2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChatMessage> getUnreadMessagesByUserId(Long userId) {
|
||||
return chatMessageRepository.findUnreadMessagesByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markAsRead(Long messageId) {
|
||||
Optional<ChatMessage> messageOpt = chatMessageRepository.findById(messageId);
|
||||
messageOpt.ifPresent(message -> {
|
||||
message.setRead(true);
|
||||
chatMessageRepository.save(message);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markAllAsRead(Long userId, Long senderId) {
|
||||
List<ChatMessage> messages = chatMessageRepository.findConversationBetweenUsers(userId, senderId);
|
||||
for (ChatMessage message : messages) {
|
||||
if (message.getReceiver().getId().equals(userId) && !message.isRead()) {
|
||||
message.setRead(true);
|
||||
chatMessageRepository.save(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getUserConversations(Long userId) {
|
||||
return chatMessageRepository.findUserConversations(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ChatMessage> findById(Long id) {
|
||||
return chatMessageRepository.findById(id);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,133 @@
|
||||
package com.example.secondhandmarket.service.impl;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.Item;
|
||||
import com.example.secondhandmarket.repository.ItemRepository;
|
||||
import com.example.secondhandmarket.service.ItemService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class ItemServiceImpl implements ItemService {
|
||||
|
||||
@Autowired
|
||||
private ItemRepository itemRepository;
|
||||
|
||||
@Override
|
||||
public Item createItem(Item item) {
|
||||
item.setCreatedAt(new Date());
|
||||
item.setUpdatedAt(new Date());
|
||||
item.setViewCount(0);
|
||||
item.setLikeCount(0);
|
||||
item.setStatus("available");
|
||||
return itemRepository.save(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Item> findById(Long id) {
|
||||
return itemRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Item> findAllAvailable(Pageable pageable) {
|
||||
return itemRepository.findByStatus("available", pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Item> findByCategory(String category, Pageable pageable) {
|
||||
return itemRepository.findByCategoryAndStatus(category, "available", pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Item> findBySeller(Long sellerId, Pageable pageable) {
|
||||
return itemRepository.findBySellerIdAndStatus(sellerId, "available", pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Item> searchItems(String keyword, Pageable pageable) {
|
||||
return itemRepository.searchItems(keyword, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Item> findByPriceRange(Double minPrice, Double maxPrice, Pageable pageable) {
|
||||
return itemRepository.findByPriceRange(minPrice, maxPrice, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item updateItem(Item item) {
|
||||
Optional<Item> existingItemOpt = itemRepository.findById(item.getId());
|
||||
if (!existingItemOpt.isPresent()) {
|
||||
throw new RuntimeException("商品不存在");
|
||||
}
|
||||
|
||||
Item existingItem = existingItemOpt.get();
|
||||
// 更新字段
|
||||
if (item.getTitle() != null) {
|
||||
existingItem.setTitle(item.getTitle());
|
||||
}
|
||||
if (item.getDescription() != null) {
|
||||
existingItem.setDescription(item.getDescription());
|
||||
}
|
||||
if (item.getPrice() != null) {
|
||||
existingItem.setPrice(item.getPrice());
|
||||
}
|
||||
if (item.getCategory() != null) {
|
||||
existingItem.setCategory(item.getCategory());
|
||||
}
|
||||
if (item.getSubcategory() != null) {
|
||||
existingItem.setSubcategory(item.getSubcategory());
|
||||
}
|
||||
if (item.getCondition() != null) {
|
||||
existingItem.setCondition(item.getCondition());
|
||||
}
|
||||
if (item.getImages() != null) {
|
||||
existingItem.setImages(item.getImages());
|
||||
}
|
||||
|
||||
existingItem.setUpdatedAt(new Date());
|
||||
return itemRepository.save(existingItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteItem(Long id) {
|
||||
if (!itemRepository.existsById(id)) {
|
||||
throw new RuntimeException("商品不存在");
|
||||
}
|
||||
itemRepository.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeStatus(Long id, String status) {
|
||||
Optional<Item> itemOpt = itemRepository.findById(id);
|
||||
if (itemOpt.isPresent()) {
|
||||
Item item = itemOpt.get();
|
||||
item.setStatus(status);
|
||||
item.setUpdatedAt(new Date());
|
||||
itemRepository.save(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementViewCount(Long id) {
|
||||
Optional<Item> itemOpt = itemRepository.findById(id);
|
||||
if (itemOpt.isPresent()) {
|
||||
Item item = itemOpt.get();
|
||||
item.setViewCount(item.getViewCount() + 1);
|
||||
itemRepository.save(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementLikeCount(Long id) {
|
||||
Optional<Item> itemOpt = itemRepository.findById(id);
|
||||
if (itemOpt.isPresent()) {
|
||||
Item item = itemOpt.get();
|
||||
item.setLikeCount(item.getLikeCount() + 1);
|
||||
itemRepository.save(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package com.example.secondhandmarket.service.impl;
|
||||
|
||||
import com.example.secondhandmarket.model.entity.User;
|
||||
import com.example.secondhandmarket.repository.UserRepository;
|
||||
import com.example.secondhandmarket.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private BCryptPasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public User register(User user) {
|
||||
// 检查手机号是否已存在
|
||||
if (existsByPhone(user.getPhone())) {
|
||||
throw new RuntimeException("手机号已被注册");
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
user.setPassword(passwordEncoder.encode(user.getPassword()));
|
||||
user.setRegisterTime(new Date());
|
||||
user.setLastLoginTime(new Date());
|
||||
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> login(String phone, String password) {
|
||||
Optional<User> userOpt = userRepository.findByPhone(phone);
|
||||
if (userOpt.isPresent()) {
|
||||
User user = userOpt.get();
|
||||
if (passwordEncoder.matches(password, user.getPassword())) {
|
||||
// 更新最后登录时间
|
||||
updateLastLoginTime(user.getId());
|
||||
return Optional.of(user);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> findById(Long id) {
|
||||
return userRepository.findById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> findByPhone(String phone) {
|
||||
return userRepository.findByPhone(phone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User update(User user) {
|
||||
Optional<User> existingUserOpt = userRepository.findById(user.getId());
|
||||
if (!existingUserOpt.isPresent()) {
|
||||
throw new RuntimeException("用户不存在");
|
||||
}
|
||||
|
||||
User existingUser = existingUserOpt.get();
|
||||
// 只更新允许修改的字段
|
||||
if (user.getUsername() != null) {
|
||||
existingUser.setUsername(user.getUsername());
|
||||
}
|
||||
if (user.getAvatar() != null) {
|
||||
existingUser.setAvatar(user.getAvatar());
|
||||
}
|
||||
if (user.getBio() != null) {
|
||||
existingUser.setBio(user.getBio());
|
||||
}
|
||||
|
||||
return userRepository.save(existingUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByPhone(String phone) {
|
||||
return userRepository.existsByPhone(phone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLastLoginTime(Long userId) {
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
userOpt.ifPresent(user -> {
|
||||
user.setLastLoginTime(new Date());
|
||||
userRepository.save(user);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.example.secondhandmarket;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class SecondHandMarketApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
// 测试应用上下文是否能正常加载
|
||||
}
|
||||
|
||||
}
|
||||
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 229 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 184 KiB |