feat: add group buy secondhand and file storage
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package com.linhelp.feedback;
|
||||
|
||||
import com.linhelp.common.api.ApiResponse;
|
||||
import com.linhelp.common.security.AuthService;
|
||||
import com.linhelp.common.security.CurrentUser;
|
||||
import com.linhelp.common.security.RequireRole;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class FeedbackController {
|
||||
private final FeedbackService feedbackService;
|
||||
private final AuthService authService;
|
||||
|
||||
public FeedbackController(FeedbackService feedbackService, AuthService authService) {
|
||||
this.feedbackService = feedbackService;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping("/api/mini/feedback")
|
||||
public ApiResponse<FeedbackResponse> create(@Valid @RequestBody FeedbackRequest request) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
request.setTenantId(user.getTenantId());
|
||||
request.setCommunityId(user.getCommunityId());
|
||||
return ApiResponse.ok(feedbackService.create(user.getUserId(), request));
|
||||
}
|
||||
|
||||
@GetMapping("/api/admin/feedback")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN"})
|
||||
public ApiResponse<List<FeedbackResponse>> listAdmin(@RequestParam(required = false) String status) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(feedbackService.listAdmin(user.getCommunityId(), status));
|
||||
}
|
||||
|
||||
@PutMapping("/api/admin/feedback/{id}/process")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN"})
|
||||
public ApiResponse<FeedbackResponse> process(@PathVariable Long id, @RequestBody FeedbackProcessRequest request) {
|
||||
return ApiResponse.ok(feedbackService.process(id, request.getProcessNote()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.linhelp.feedback;
|
||||
|
||||
public class FeedbackProcessRequest {
|
||||
private String processNote;
|
||||
|
||||
public String getProcessNote() {
|
||||
return processNote;
|
||||
}
|
||||
|
||||
public void setProcessNote(String processNote) {
|
||||
this.processNote = processNote;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.linhelp.feedback;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
public class FeedbackRequest {
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
private String category;
|
||||
|
||||
@NotBlank
|
||||
private String content;
|
||||
|
||||
private String contactPhone;
|
||||
private String bizType;
|
||||
private Long bizOrderId;
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public void setCommunityId(Long communityId) {
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getContactPhone() {
|
||||
return contactPhone;
|
||||
}
|
||||
|
||||
public void setContactPhone(String contactPhone) {
|
||||
this.contactPhone = contactPhone;
|
||||
}
|
||||
|
||||
public String getBizType() {
|
||||
return bizType;
|
||||
}
|
||||
|
||||
public void setBizType(String bizType) {
|
||||
this.bizType = bizType;
|
||||
}
|
||||
|
||||
public Long getBizOrderId() {
|
||||
return bizOrderId;
|
||||
}
|
||||
|
||||
public void setBizOrderId(Long bizOrderId) {
|
||||
this.bizOrderId = bizOrderId;
|
||||
}
|
||||
}
|
||||
123
backend/src/main/java/com/linhelp/feedback/FeedbackResponse.java
Normal file
123
backend/src/main/java/com/linhelp/feedback/FeedbackResponse.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package com.linhelp.feedback;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class FeedbackResponse {
|
||||
private Long id;
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
private Long userId;
|
||||
private String category;
|
||||
private String content;
|
||||
private String contactPhone;
|
||||
private String bizType;
|
||||
private Long bizOrderId;
|
||||
private String status;
|
||||
private String processNote;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public void setCommunityId(Long communityId) {
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getContactPhone() {
|
||||
return contactPhone;
|
||||
}
|
||||
|
||||
public void setContactPhone(String contactPhone) {
|
||||
this.contactPhone = contactPhone;
|
||||
}
|
||||
|
||||
public String getBizType() {
|
||||
return bizType;
|
||||
}
|
||||
|
||||
public void setBizType(String bizType) {
|
||||
this.bizType = bizType;
|
||||
}
|
||||
|
||||
public Long getBizOrderId() {
|
||||
return bizOrderId;
|
||||
}
|
||||
|
||||
public void setBizOrderId(Long bizOrderId) {
|
||||
this.bizOrderId = bizOrderId;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getProcessNote() {
|
||||
return processNote;
|
||||
}
|
||||
|
||||
public void setProcessNote(String processNote) {
|
||||
this.processNote = processNote;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.linhelp.feedback;
|
||||
|
||||
import com.linhelp.common.exception.BizException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@Service
|
||||
public class FeedbackService {
|
||||
public static final String STATUS_PENDING = "PENDING";
|
||||
public static final String STATUS_PROCESSED = "PROCESSED";
|
||||
|
||||
private final AtomicLong ids = new AtomicLong(1L);
|
||||
private final Map<Long, FeedbackResponse> feedbacks = new LinkedHashMap<Long, FeedbackResponse>();
|
||||
|
||||
public synchronized FeedbackResponse create(Long userId, FeedbackRequest request) {
|
||||
FeedbackResponse feedback = new FeedbackResponse();
|
||||
feedback.setId(ids.getAndIncrement());
|
||||
feedback.setTenantId(request.getTenantId());
|
||||
feedback.setCommunityId(request.getCommunityId());
|
||||
feedback.setUserId(userId);
|
||||
feedback.setCategory(request.getCategory());
|
||||
feedback.setContent(request.getContent());
|
||||
feedback.setContactPhone(request.getContactPhone());
|
||||
feedback.setBizType(request.getBizType());
|
||||
feedback.setBizOrderId(request.getBizOrderId());
|
||||
feedback.setStatus(STATUS_PENDING);
|
||||
feedback.setCreatedAt(LocalDateTime.now());
|
||||
feedback.setUpdatedAt(feedback.getCreatedAt());
|
||||
feedbacks.put(feedback.getId(), feedback);
|
||||
return copy(feedback);
|
||||
}
|
||||
|
||||
public synchronized List<FeedbackResponse> listAdmin(Long communityId, String status) {
|
||||
List<FeedbackResponse> result = new ArrayList<FeedbackResponse>();
|
||||
for (FeedbackResponse feedback : feedbacks.values()) {
|
||||
if (!communityId.equals(feedback.getCommunityId())) {
|
||||
continue;
|
||||
}
|
||||
if (status != null && !status.equals(feedback.getStatus())) {
|
||||
continue;
|
||||
}
|
||||
result.add(copy(feedback));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized FeedbackResponse process(Long id, String processNote) {
|
||||
FeedbackResponse feedback = feedbacks.get(id);
|
||||
if (feedback == null) {
|
||||
throw new BizException(404, "反馈不存在");
|
||||
}
|
||||
feedback.setStatus(STATUS_PROCESSED);
|
||||
feedback.setProcessNote(processNote);
|
||||
feedback.setUpdatedAt(LocalDateTime.now());
|
||||
return copy(feedback);
|
||||
}
|
||||
|
||||
private FeedbackResponse copy(FeedbackResponse source) {
|
||||
FeedbackResponse target = new FeedbackResponse();
|
||||
target.setId(source.getId());
|
||||
target.setTenantId(source.getTenantId());
|
||||
target.setCommunityId(source.getCommunityId());
|
||||
target.setUserId(source.getUserId());
|
||||
target.setCategory(source.getCategory());
|
||||
target.setContent(source.getContent());
|
||||
target.setContactPhone(source.getContactPhone());
|
||||
target.setBizType(source.getBizType());
|
||||
target.setBizOrderId(source.getBizOrderId());
|
||||
target.setStatus(source.getStatus());
|
||||
target.setProcessNote(source.getProcessNote());
|
||||
target.setCreatedAt(source.getCreatedAt());
|
||||
target.setUpdatedAt(source.getUpdatedAt());
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.linhelp.groupbuy;
|
||||
|
||||
import com.linhelp.common.api.ApiResponse;
|
||||
import com.linhelp.common.security.AuthService;
|
||||
import com.linhelp.common.security.CurrentUser;
|
||||
import com.linhelp.common.security.RequireRole;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class GroupBuyController {
|
||||
private final GroupBuyService groupBuyService;
|
||||
private final GroupBuyOrderService orderService;
|
||||
private final AuthService authService;
|
||||
|
||||
public GroupBuyController(GroupBuyService groupBuyService,
|
||||
GroupBuyOrderService orderService,
|
||||
AuthService authService) {
|
||||
this.groupBuyService = groupBuyService;
|
||||
this.orderService = orderService;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@GetMapping("/api/admin/group-buys")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN", "MERCHANT"})
|
||||
public ApiResponse<List<GroupBuyResponse>> listAdmin() {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(groupBuyService.listAdmin(user.getCommunityId()));
|
||||
}
|
||||
|
||||
@PostMapping("/api/admin/group-buys")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN", "MERCHANT"})
|
||||
public ApiResponse<GroupBuyResponse> create(@Valid @RequestBody GroupBuyRequest request) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
request.setTenantId(user.getTenantId());
|
||||
request.setCommunityId(user.getCommunityId());
|
||||
return ApiResponse.ok(groupBuyService.create(request));
|
||||
}
|
||||
|
||||
@PutMapping("/api/admin/group-buys/{id}")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN", "MERCHANT"})
|
||||
public ApiResponse<GroupBuyResponse> update(@PathVariable Long id, @Valid @RequestBody GroupBuyRequest request) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
request.setTenantId(user.getTenantId());
|
||||
request.setCommunityId(user.getCommunityId());
|
||||
return ApiResponse.ok(groupBuyService.update(id, request));
|
||||
}
|
||||
|
||||
@PutMapping("/api/admin/group-buys/{id}/start")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN", "MERCHANT"})
|
||||
public ApiResponse<GroupBuyResponse> start(@PathVariable Long id) {
|
||||
return ApiResponse.ok(groupBuyService.start(id));
|
||||
}
|
||||
|
||||
@PutMapping("/api/admin/group-buys/{id}/close")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN", "MERCHANT"})
|
||||
public ApiResponse<GroupBuyResponse> close(@PathVariable Long id) {
|
||||
return ApiResponse.ok(groupBuyService.close(id));
|
||||
}
|
||||
|
||||
@PutMapping("/api/admin/group-buys/{id}/complete")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN", "MERCHANT"})
|
||||
public ApiResponse<GroupBuyResponse> completeGroupBuy(@PathVariable Long id) {
|
||||
return ApiResponse.ok(groupBuyService.complete(id));
|
||||
}
|
||||
|
||||
@GetMapping("/api/admin/group-buys/{id}/orders")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN", "MERCHANT"})
|
||||
public ApiResponse<List<GroupBuyOrderResponse>> listOrders(@PathVariable Long id) {
|
||||
return ApiResponse.ok(orderService.listByGroupBuy(id));
|
||||
}
|
||||
|
||||
@GetMapping("/api/mini/group-buys")
|
||||
public ApiResponse<List<GroupBuyResponse>> listMini() {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(groupBuyService.listMini(user.getCommunityId()));
|
||||
}
|
||||
|
||||
@GetMapping("/api/mini/group-buys/{id}")
|
||||
public ApiResponse<GroupBuyResponse> detail(@PathVariable Long id) {
|
||||
return ApiResponse.ok(groupBuyService.detail(id));
|
||||
}
|
||||
|
||||
@PostMapping("/api/mini/group-buy-orders")
|
||||
public ApiResponse<GroupBuyOrderResponse> createOrder(@Valid @RequestBody GroupBuyOrderRequest request) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
request.setTenantId(user.getTenantId());
|
||||
request.setCommunityId(user.getCommunityId());
|
||||
return ApiResponse.ok(orderService.create(user.getUserId(), request));
|
||||
}
|
||||
|
||||
@GetMapping("/api/mini/group-buy-orders")
|
||||
public ApiResponse<List<GroupBuyOrderResponse>> listMine() {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(orderService.listMine(user.getUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/api/mini/group-buy-orders/{id}")
|
||||
public ApiResponse<GroupBuyOrderResponse> orderDetail(@PathVariable Long id) {
|
||||
return ApiResponse.ok(orderService.detail(id));
|
||||
}
|
||||
|
||||
@PutMapping("/api/mini/group-buy-orders/{id}/cancel")
|
||||
public ApiResponse<GroupBuyOrderResponse> cancel(@PathVariable Long id) {
|
||||
return ApiResponse.ok(orderService.cancel(id));
|
||||
}
|
||||
|
||||
@PutMapping("/api/mini/group-buy-orders/{id}/complete")
|
||||
public ApiResponse<GroupBuyOrderResponse> completeOrder(@PathVariable Long id) {
|
||||
return ApiResponse.ok(orderService.complete(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.linhelp.groupbuy;
|
||||
|
||||
import com.linhelp.common.enums.DeliveryMethod;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class GroupBuyOrderRequest {
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
|
||||
@NotNull
|
||||
private Long groupBuyId;
|
||||
|
||||
private Long addressId;
|
||||
|
||||
@Min(1)
|
||||
private int quantity;
|
||||
|
||||
@NotNull
|
||||
private DeliveryMethod deliveryMethod = DeliveryMethod.IMMEDIATE;
|
||||
|
||||
private String remark;
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public void setCommunityId(Long communityId) {
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
public Long getGroupBuyId() {
|
||||
return groupBuyId;
|
||||
}
|
||||
|
||||
public void setGroupBuyId(Long groupBuyId) {
|
||||
this.groupBuyId = groupBuyId;
|
||||
}
|
||||
|
||||
public Long getAddressId() {
|
||||
return addressId;
|
||||
}
|
||||
|
||||
public void setAddressId(Long addressId) {
|
||||
this.addressId = addressId;
|
||||
}
|
||||
|
||||
public int getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(int quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public DeliveryMethod getDeliveryMethod() {
|
||||
return deliveryMethod;
|
||||
}
|
||||
|
||||
public void setDeliveryMethod(DeliveryMethod deliveryMethod) {
|
||||
this.deliveryMethod = deliveryMethod;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.linhelp.groupbuy;
|
||||
|
||||
import com.linhelp.common.enums.DeliveryMethod;
|
||||
import com.linhelp.common.enums.GroupBuyOrderStatus;
|
||||
import com.linhelp.common.enums.OfflinePayStatus;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class GroupBuyOrderResponse {
|
||||
private Long id;
|
||||
private String orderNo;
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
private Long userId;
|
||||
private Long groupBuyId;
|
||||
private String groupBuyTitle;
|
||||
private String coverUrl;
|
||||
private Long addressId;
|
||||
private int quantity;
|
||||
private int priceCent;
|
||||
private int amountCent;
|
||||
private DeliveryMethod deliveryMethod;
|
||||
private GroupBuyOrderStatus status;
|
||||
private OfflinePayStatus offlinePayStatus;
|
||||
private String remark;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getOrderNo() {
|
||||
return orderNo;
|
||||
}
|
||||
|
||||
public void setOrderNo(String orderNo) {
|
||||
this.orderNo = orderNo;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public void setCommunityId(Long communityId) {
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getGroupBuyId() {
|
||||
return groupBuyId;
|
||||
}
|
||||
|
||||
public void setGroupBuyId(Long groupBuyId) {
|
||||
this.groupBuyId = groupBuyId;
|
||||
}
|
||||
|
||||
public String getGroupBuyTitle() {
|
||||
return groupBuyTitle;
|
||||
}
|
||||
|
||||
public void setGroupBuyTitle(String groupBuyTitle) {
|
||||
this.groupBuyTitle = groupBuyTitle;
|
||||
}
|
||||
|
||||
public String getCoverUrl() {
|
||||
return coverUrl;
|
||||
}
|
||||
|
||||
public void setCoverUrl(String coverUrl) {
|
||||
this.coverUrl = coverUrl;
|
||||
}
|
||||
|
||||
public Long getAddressId() {
|
||||
return addressId;
|
||||
}
|
||||
|
||||
public void setAddressId(Long addressId) {
|
||||
this.addressId = addressId;
|
||||
}
|
||||
|
||||
public int getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(int quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public int getPriceCent() {
|
||||
return priceCent;
|
||||
}
|
||||
|
||||
public void setPriceCent(int priceCent) {
|
||||
this.priceCent = priceCent;
|
||||
}
|
||||
|
||||
public int getAmountCent() {
|
||||
return amountCent;
|
||||
}
|
||||
|
||||
public void setAmountCent(int amountCent) {
|
||||
this.amountCent = amountCent;
|
||||
}
|
||||
|
||||
public DeliveryMethod getDeliveryMethod() {
|
||||
return deliveryMethod;
|
||||
}
|
||||
|
||||
public void setDeliveryMethod(DeliveryMethod deliveryMethod) {
|
||||
this.deliveryMethod = deliveryMethod;
|
||||
}
|
||||
|
||||
public GroupBuyOrderStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(GroupBuyOrderStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public OfflinePayStatus getOfflinePayStatus() {
|
||||
return offlinePayStatus;
|
||||
}
|
||||
|
||||
public void setOfflinePayStatus(OfflinePayStatus offlinePayStatus) {
|
||||
this.offlinePayStatus = offlinePayStatus;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.linhelp.groupbuy;
|
||||
|
||||
import com.linhelp.common.enums.DeliveryMethod;
|
||||
import com.linhelp.common.enums.GroupBuyOrderStatus;
|
||||
import com.linhelp.common.enums.OfflinePayStatus;
|
||||
import com.linhelp.common.exception.BizException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@Service
|
||||
public class GroupBuyOrderService {
|
||||
private final GroupBuyService groupBuyService;
|
||||
private final AtomicLong ids = new AtomicLong(1L);
|
||||
private final Map<Long, GroupBuyOrderResponse> orders = new LinkedHashMap<Long, GroupBuyOrderResponse>();
|
||||
|
||||
public GroupBuyOrderService(GroupBuyService groupBuyService) {
|
||||
this.groupBuyService = groupBuyService;
|
||||
}
|
||||
|
||||
public synchronized GroupBuyOrderResponse create(Long userId, GroupBuyOrderRequest request) {
|
||||
if (request.getDeliveryMethod() != DeliveryMethod.SELF_PICKUP && request.getAddressId() == null) {
|
||||
throw new BizException("配送地址不能为空");
|
||||
}
|
||||
GroupBuyResponse groupBuy = groupBuyService.reserveForOrder(request.getGroupBuyId(), request.getQuantity());
|
||||
Long orderId = ids.getAndIncrement();
|
||||
GroupBuyOrderResponse order = new GroupBuyOrderResponse();
|
||||
order.setId(orderId);
|
||||
order.setOrderNo("GB" + String.format("%08d", orderId));
|
||||
order.setTenantId(request.getTenantId());
|
||||
order.setCommunityId(request.getCommunityId());
|
||||
order.setUserId(userId);
|
||||
order.setGroupBuyId(groupBuy.getId());
|
||||
order.setGroupBuyTitle(groupBuy.getTitle());
|
||||
order.setCoverUrl(groupBuy.getCoverUrl());
|
||||
order.setAddressId(request.getAddressId());
|
||||
order.setQuantity(request.getQuantity());
|
||||
order.setPriceCent(groupBuy.getPriceCent());
|
||||
order.setAmountCent(groupBuy.getPriceCent() * request.getQuantity());
|
||||
order.setDeliveryMethod(request.getDeliveryMethod());
|
||||
order.setStatus(GroupBuyOrderStatus.PENDING_CONFIRM);
|
||||
order.setOfflinePayStatus(OfflinePayStatus.UNPAID);
|
||||
order.setRemark(request.getRemark());
|
||||
order.setCreatedAt(LocalDateTime.now());
|
||||
order.setUpdatedAt(order.getCreatedAt());
|
||||
orders.put(order.getId(), order);
|
||||
return copy(order);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyOrderResponse detail(Long id) {
|
||||
return copy(require(id));
|
||||
}
|
||||
|
||||
public synchronized List<GroupBuyOrderResponse> listMine(Long userId) {
|
||||
List<GroupBuyOrderResponse> result = new ArrayList<GroupBuyOrderResponse>();
|
||||
for (GroupBuyOrderResponse order : orders.values()) {
|
||||
if (userId.equals(order.getUserId())) {
|
||||
result.add(copy(order));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized List<GroupBuyOrderResponse> listByGroupBuy(Long groupBuyId) {
|
||||
List<GroupBuyOrderResponse> result = new ArrayList<GroupBuyOrderResponse>();
|
||||
for (GroupBuyOrderResponse order : orders.values()) {
|
||||
if (groupBuyId.equals(order.getGroupBuyId())) {
|
||||
result.add(copy(order));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized GroupBuyOrderResponse confirm(Long id) {
|
||||
GroupBuyOrderResponse order = require(id);
|
||||
move(order, GroupBuyOrderStatus.PENDING_CONFIRM, GroupBuyOrderStatus.CONFIRMED);
|
||||
return copy(order);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyOrderResponse markReady(Long id) {
|
||||
GroupBuyOrderResponse order = require(id);
|
||||
GroupBuyOrderStatus target = DeliveryMethod.SELF_PICKUP.equals(order.getDeliveryMethod())
|
||||
? GroupBuyOrderStatus.PENDING_PICKUP
|
||||
: GroupBuyOrderStatus.PENDING_DELIVERY;
|
||||
move(order, GroupBuyOrderStatus.CONFIRMED, target);
|
||||
return copy(order);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyOrderResponse markDelivering(Long id) {
|
||||
GroupBuyOrderResponse order = require(id);
|
||||
move(order, GroupBuyOrderStatus.PENDING_DELIVERY, GroupBuyOrderStatus.DELIVERING);
|
||||
return copy(order);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyOrderResponse complete(Long id) {
|
||||
GroupBuyOrderResponse order = require(id);
|
||||
if (order.getStatus() != GroupBuyOrderStatus.PENDING_PICKUP && order.getStatus() != GroupBuyOrderStatus.DELIVERING) {
|
||||
throw new BizException("订单状态不允许操作");
|
||||
}
|
||||
order.setStatus(GroupBuyOrderStatus.COMPLETED);
|
||||
order.setUpdatedAt(LocalDateTime.now());
|
||||
return copy(order);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyOrderResponse cancel(Long id) {
|
||||
GroupBuyOrderResponse order = require(id);
|
||||
if (order.getStatus() == GroupBuyOrderStatus.COMPLETED) {
|
||||
throw new BizException("订单状态不允许操作");
|
||||
}
|
||||
order.setStatus(GroupBuyOrderStatus.CANCELED);
|
||||
order.setUpdatedAt(LocalDateTime.now());
|
||||
return copy(order);
|
||||
}
|
||||
|
||||
private void move(GroupBuyOrderResponse order, GroupBuyOrderStatus expected, GroupBuyOrderStatus target) {
|
||||
if (order.getStatus() != expected) {
|
||||
throw new BizException("订单状态不允许操作");
|
||||
}
|
||||
order.setStatus(target);
|
||||
order.setUpdatedAt(LocalDateTime.now());
|
||||
}
|
||||
|
||||
private GroupBuyOrderResponse require(Long id) {
|
||||
GroupBuyOrderResponse order = orders.get(id);
|
||||
if (order == null) {
|
||||
throw new BizException(404, "团购订单不存在");
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
private GroupBuyOrderResponse copy(GroupBuyOrderResponse source) {
|
||||
GroupBuyOrderResponse target = new GroupBuyOrderResponse();
|
||||
target.setId(source.getId());
|
||||
target.setOrderNo(source.getOrderNo());
|
||||
target.setTenantId(source.getTenantId());
|
||||
target.setCommunityId(source.getCommunityId());
|
||||
target.setUserId(source.getUserId());
|
||||
target.setGroupBuyId(source.getGroupBuyId());
|
||||
target.setGroupBuyTitle(source.getGroupBuyTitle());
|
||||
target.setCoverUrl(source.getCoverUrl());
|
||||
target.setAddressId(source.getAddressId());
|
||||
target.setQuantity(source.getQuantity());
|
||||
target.setPriceCent(source.getPriceCent());
|
||||
target.setAmountCent(source.getAmountCent());
|
||||
target.setDeliveryMethod(source.getDeliveryMethod());
|
||||
target.setStatus(source.getStatus());
|
||||
target.setOfflinePayStatus(source.getOfflinePayStatus());
|
||||
target.setRemark(source.getRemark());
|
||||
target.setCreatedAt(source.getCreatedAt());
|
||||
target.setUpdatedAt(source.getUpdatedAt());
|
||||
return target;
|
||||
}
|
||||
}
|
||||
121
backend/src/main/java/com/linhelp/groupbuy/GroupBuyRequest.java
Normal file
121
backend/src/main/java/com/linhelp/groupbuy/GroupBuyRequest.java
Normal file
@@ -0,0 +1,121 @@
|
||||
package com.linhelp.groupbuy;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class GroupBuyRequest {
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
|
||||
@NotBlank
|
||||
private String title;
|
||||
|
||||
private String coverUrl;
|
||||
private String description;
|
||||
|
||||
@Min(0)
|
||||
private int priceCent;
|
||||
|
||||
private Integer originPriceCent;
|
||||
|
||||
@Min(0)
|
||||
private int stock;
|
||||
|
||||
@NotNull
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@NotNull
|
||||
private LocalDateTime endTime;
|
||||
|
||||
private String pickupAddress;
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public void setCommunityId(Long communityId) {
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getCoverUrl() {
|
||||
return coverUrl;
|
||||
}
|
||||
|
||||
public void setCoverUrl(String coverUrl) {
|
||||
this.coverUrl = coverUrl;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public int getPriceCent() {
|
||||
return priceCent;
|
||||
}
|
||||
|
||||
public void setPriceCent(int priceCent) {
|
||||
this.priceCent = priceCent;
|
||||
}
|
||||
|
||||
public Integer getOriginPriceCent() {
|
||||
return originPriceCent;
|
||||
}
|
||||
|
||||
public void setOriginPriceCent(Integer originPriceCent) {
|
||||
this.originPriceCent = originPriceCent;
|
||||
}
|
||||
|
||||
public int getStock() {
|
||||
return stock;
|
||||
}
|
||||
|
||||
public void setStock(int stock) {
|
||||
this.stock = stock;
|
||||
}
|
||||
|
||||
public LocalDateTime getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(LocalDateTime startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setEndTime(LocalDateTime endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public String getPickupAddress() {
|
||||
return pickupAddress;
|
||||
}
|
||||
|
||||
public void setPickupAddress(String pickupAddress) {
|
||||
this.pickupAddress = pickupAddress;
|
||||
}
|
||||
}
|
||||
132
backend/src/main/java/com/linhelp/groupbuy/GroupBuyResponse.java
Normal file
132
backend/src/main/java/com/linhelp/groupbuy/GroupBuyResponse.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package com.linhelp.groupbuy;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class GroupBuyResponse {
|
||||
private Long id;
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
private String title;
|
||||
private String coverUrl;
|
||||
private String description;
|
||||
private int priceCent;
|
||||
private Integer originPriceCent;
|
||||
private int stock;
|
||||
private int soldCount;
|
||||
private String status;
|
||||
private LocalDateTime startTime;
|
||||
private LocalDateTime endTime;
|
||||
private String pickupAddress;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public void setCommunityId(Long communityId) {
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getCoverUrl() {
|
||||
return coverUrl;
|
||||
}
|
||||
|
||||
public void setCoverUrl(String coverUrl) {
|
||||
this.coverUrl = coverUrl;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public int getPriceCent() {
|
||||
return priceCent;
|
||||
}
|
||||
|
||||
public void setPriceCent(int priceCent) {
|
||||
this.priceCent = priceCent;
|
||||
}
|
||||
|
||||
public Integer getOriginPriceCent() {
|
||||
return originPriceCent;
|
||||
}
|
||||
|
||||
public void setOriginPriceCent(Integer originPriceCent) {
|
||||
this.originPriceCent = originPriceCent;
|
||||
}
|
||||
|
||||
public int getStock() {
|
||||
return stock;
|
||||
}
|
||||
|
||||
public void setStock(int stock) {
|
||||
this.stock = stock;
|
||||
}
|
||||
|
||||
public int getSoldCount() {
|
||||
return soldCount;
|
||||
}
|
||||
|
||||
public void setSoldCount(int soldCount) {
|
||||
this.soldCount = soldCount;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public LocalDateTime getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(LocalDateTime startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setEndTime(LocalDateTime endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public String getPickupAddress() {
|
||||
return pickupAddress;
|
||||
}
|
||||
|
||||
public void setPickupAddress(String pickupAddress) {
|
||||
this.pickupAddress = pickupAddress;
|
||||
}
|
||||
}
|
||||
141
backend/src/main/java/com/linhelp/groupbuy/GroupBuyService.java
Normal file
141
backend/src/main/java/com/linhelp/groupbuy/GroupBuyService.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package com.linhelp.groupbuy;
|
||||
|
||||
import com.linhelp.common.exception.BizException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@Service
|
||||
public class GroupBuyService {
|
||||
public static final String STATUS_DRAFT = "DRAFT";
|
||||
public static final String STATUS_ONGOING = "ONGOING";
|
||||
public static final String STATUS_CLOSED = "CLOSED";
|
||||
public static final String STATUS_COMPLETED = "COMPLETED";
|
||||
|
||||
private final AtomicLong ids = new AtomicLong(1L);
|
||||
private final Map<Long, GroupBuyResponse> groupBuys = new LinkedHashMap<Long, GroupBuyResponse>();
|
||||
|
||||
public synchronized GroupBuyResponse create(GroupBuyRequest request) {
|
||||
GroupBuyResponse groupBuy = new GroupBuyResponse();
|
||||
groupBuy.setId(ids.getAndIncrement());
|
||||
groupBuy.setTenantId(request.getTenantId());
|
||||
groupBuy.setCommunityId(request.getCommunityId());
|
||||
copyFields(request, groupBuy);
|
||||
groupBuy.setStatus(STATUS_DRAFT);
|
||||
groupBuys.put(groupBuy.getId(), groupBuy);
|
||||
return copy(groupBuy);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyResponse update(Long id, GroupBuyRequest request) {
|
||||
GroupBuyResponse groupBuy = require(id);
|
||||
if (request.getTenantId() != null) {
|
||||
groupBuy.setTenantId(request.getTenantId());
|
||||
}
|
||||
if (request.getCommunityId() != null) {
|
||||
groupBuy.setCommunityId(request.getCommunityId());
|
||||
}
|
||||
copyFields(request, groupBuy);
|
||||
return copy(groupBuy);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyResponse start(Long id) {
|
||||
GroupBuyResponse groupBuy = require(id);
|
||||
groupBuy.setStatus(STATUS_ONGOING);
|
||||
return copy(groupBuy);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyResponse close(Long id) {
|
||||
GroupBuyResponse groupBuy = require(id);
|
||||
groupBuy.setStatus(STATUS_CLOSED);
|
||||
return copy(groupBuy);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyResponse complete(Long id) {
|
||||
GroupBuyResponse groupBuy = require(id);
|
||||
groupBuy.setStatus(STATUS_COMPLETED);
|
||||
return copy(groupBuy);
|
||||
}
|
||||
|
||||
public synchronized GroupBuyResponse detail(Long id) {
|
||||
return copy(require(id));
|
||||
}
|
||||
|
||||
public synchronized List<GroupBuyResponse> listAdmin(Long communityId) {
|
||||
List<GroupBuyResponse> result = new ArrayList<GroupBuyResponse>();
|
||||
for (GroupBuyResponse groupBuy : groupBuys.values()) {
|
||||
if (communityId.equals(groupBuy.getCommunityId())) {
|
||||
result.add(copy(groupBuy));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized List<GroupBuyResponse> listMini(Long communityId) {
|
||||
List<GroupBuyResponse> result = new ArrayList<GroupBuyResponse>();
|
||||
for (GroupBuyResponse groupBuy : groupBuys.values()) {
|
||||
if (communityId.equals(groupBuy.getCommunityId()) && STATUS_ONGOING.equals(groupBuy.getStatus())) {
|
||||
result.add(copy(groupBuy));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized GroupBuyResponse reserveForOrder(Long id, int quantity) {
|
||||
if (quantity <= 0) {
|
||||
throw new BizException("购买数量必须大于0");
|
||||
}
|
||||
GroupBuyResponse groupBuy = require(id);
|
||||
if (!STATUS_ONGOING.equals(groupBuy.getStatus()) || LocalDateTime.now().isAfter(groupBuy.getEndTime())) {
|
||||
throw new BizException("团购已截单");
|
||||
}
|
||||
if (groupBuy.getSoldCount() + quantity > groupBuy.getStock()) {
|
||||
throw new BizException("团购库存不足");
|
||||
}
|
||||
groupBuy.setSoldCount(groupBuy.getSoldCount() + quantity);
|
||||
return copy(groupBuy);
|
||||
}
|
||||
|
||||
private GroupBuyResponse require(Long id) {
|
||||
GroupBuyResponse groupBuy = groupBuys.get(id);
|
||||
if (groupBuy == null) {
|
||||
throw new BizException(404, "团购不存在");
|
||||
}
|
||||
return groupBuy;
|
||||
}
|
||||
|
||||
private void copyFields(GroupBuyRequest request, GroupBuyResponse groupBuy) {
|
||||
groupBuy.setTitle(request.getTitle());
|
||||
groupBuy.setCoverUrl(request.getCoverUrl());
|
||||
groupBuy.setDescription(request.getDescription());
|
||||
groupBuy.setPriceCent(request.getPriceCent());
|
||||
groupBuy.setOriginPriceCent(request.getOriginPriceCent());
|
||||
groupBuy.setStock(request.getStock());
|
||||
groupBuy.setStartTime(request.getStartTime());
|
||||
groupBuy.setEndTime(request.getEndTime());
|
||||
groupBuy.setPickupAddress(request.getPickupAddress());
|
||||
}
|
||||
|
||||
private GroupBuyResponse copy(GroupBuyResponse source) {
|
||||
GroupBuyResponse target = new GroupBuyResponse();
|
||||
target.setId(source.getId());
|
||||
target.setTenantId(source.getTenantId());
|
||||
target.setCommunityId(source.getCommunityId());
|
||||
target.setTitle(source.getTitle());
|
||||
target.setCoverUrl(source.getCoverUrl());
|
||||
target.setDescription(source.getDescription());
|
||||
target.setPriceCent(source.getPriceCent());
|
||||
target.setOriginPriceCent(source.getOriginPriceCent());
|
||||
target.setStock(source.getStock());
|
||||
target.setSoldCount(source.getSoldCount());
|
||||
target.setStatus(source.getStatus());
|
||||
target.setStartTime(source.getStartTime());
|
||||
target.setEndTime(source.getEndTime());
|
||||
target.setPickupAddress(source.getPickupAddress());
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.linhelp.secondhand;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
public class SecondGoodsCommentRequest {
|
||||
@NotBlank
|
||||
private String content;
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.linhelp.secondhand;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SecondGoodsCommentResponse {
|
||||
private Long id;
|
||||
private Long goodsId;
|
||||
private Long userId;
|
||||
private String content;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getGoodsId() {
|
||||
return goodsId;
|
||||
}
|
||||
|
||||
public void setGoodsId(Long goodsId) {
|
||||
this.goodsId = goodsId;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.linhelp.secondhand;
|
||||
|
||||
import com.linhelp.common.api.ApiResponse;
|
||||
import com.linhelp.common.enums.SecondGoodsStatus;
|
||||
import com.linhelp.common.security.AuthService;
|
||||
import com.linhelp.common.security.CurrentUser;
|
||||
import com.linhelp.common.security.RequireRole;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class SecondGoodsController {
|
||||
private final SecondGoodsService secondGoodsService;
|
||||
private final AuthService authService;
|
||||
|
||||
public SecondGoodsController(SecondGoodsService secondGoodsService, AuthService authService) {
|
||||
this.secondGoodsService = secondGoodsService;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@GetMapping("/api/admin/second-goods")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN"})
|
||||
public ApiResponse<List<SecondGoodsResponse>> listAdmin(@RequestParam(required = false) SecondGoodsStatus status) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(secondGoodsService.listAdmin(user.getCommunityId(), status));
|
||||
}
|
||||
|
||||
@PutMapping("/api/admin/second-goods/{id}/approve")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN"})
|
||||
public ApiResponse<SecondGoodsResponse> approve(@PathVariable Long id) {
|
||||
return ApiResponse.ok(secondGoodsService.approve(id));
|
||||
}
|
||||
|
||||
@PutMapping("/api/admin/second-goods/{id}/reject")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN"})
|
||||
public ApiResponse<SecondGoodsResponse> reject(@PathVariable Long id, @RequestBody SecondGoodsRejectRequest request) {
|
||||
return ApiResponse.ok(secondGoodsService.reject(id, request.getReason()));
|
||||
}
|
||||
|
||||
@PutMapping("/api/admin/second-goods/{id}/off-shelf")
|
||||
@RequireRole({"PLATFORM_ADMIN", "COMMUNITY_ADMIN"})
|
||||
public ApiResponse<SecondGoodsResponse> adminOffShelf(@PathVariable Long id) {
|
||||
return ApiResponse.ok(secondGoodsService.adminOffShelf(id));
|
||||
}
|
||||
|
||||
@GetMapping("/api/mini/second-goods")
|
||||
public ApiResponse<List<SecondGoodsResponse>> listPublic(@RequestParam(required = false) String category) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(secondGoodsService.listPublic(user.getCommunityId(), category));
|
||||
}
|
||||
|
||||
@GetMapping("/api/mini/second-goods/{id}")
|
||||
public ApiResponse<SecondGoodsResponse> detail(@PathVariable Long id) {
|
||||
return ApiResponse.ok(secondGoodsService.detail(id));
|
||||
}
|
||||
|
||||
@PostMapping("/api/mini/second-goods")
|
||||
public ApiResponse<SecondGoodsResponse> create(@Valid @RequestBody SecondGoodsRequest request) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
request.setTenantId(user.getTenantId());
|
||||
request.setCommunityId(user.getCommunityId());
|
||||
return ApiResponse.ok(secondGoodsService.create(user.getUserId(), request));
|
||||
}
|
||||
|
||||
@PutMapping("/api/mini/second-goods/{id}/off-shelf")
|
||||
public ApiResponse<SecondGoodsResponse> offShelf(@PathVariable Long id) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(secondGoodsService.offShelf(user.getUserId(), id));
|
||||
}
|
||||
|
||||
@PutMapping("/api/mini/second-goods/{id}/sold")
|
||||
public ApiResponse<SecondGoodsResponse> sold(@PathVariable Long id) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(secondGoodsService.sold(user.getUserId(), id));
|
||||
}
|
||||
|
||||
@PostMapping("/api/mini/second-goods/{id}/comments")
|
||||
public ApiResponse<SecondGoodsCommentResponse> comment(@PathVariable Long id,
|
||||
@Valid @RequestBody SecondGoodsCommentRequest request) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(secondGoodsService.comment(user.getUserId(), id, request));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.linhelp.secondhand;
|
||||
|
||||
public class SecondGoodsRejectRequest {
|
||||
private String reason;
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.linhelp.secondhand;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SecondGoodsRequest {
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
|
||||
@NotBlank
|
||||
private String title;
|
||||
|
||||
private List<String> imageUrls = new ArrayList<String>();
|
||||
|
||||
@Min(0)
|
||||
private int priceCent;
|
||||
|
||||
private String description;
|
||||
private String category;
|
||||
private String contactPhone;
|
||||
private String tradeMethod;
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public void setCommunityId(Long communityId) {
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public List<String> getImageUrls() {
|
||||
return imageUrls;
|
||||
}
|
||||
|
||||
public void setImageUrls(List<String> imageUrls) {
|
||||
this.imageUrls = imageUrls;
|
||||
}
|
||||
|
||||
public int getPriceCent() {
|
||||
return priceCent;
|
||||
}
|
||||
|
||||
public void setPriceCent(int priceCent) {
|
||||
this.priceCent = priceCent;
|
||||
}
|
||||
|
||||
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 String getContactPhone() {
|
||||
return contactPhone;
|
||||
}
|
||||
|
||||
public void setContactPhone(String contactPhone) {
|
||||
this.contactPhone = contactPhone;
|
||||
}
|
||||
|
||||
public String getTradeMethod() {
|
||||
return tradeMethod;
|
||||
}
|
||||
|
||||
public void setTradeMethod(String tradeMethod) {
|
||||
this.tradeMethod = tradeMethod;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package com.linhelp.secondhand;
|
||||
|
||||
import com.linhelp.common.enums.SecondGoodsStatus;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SecondGoodsResponse {
|
||||
private Long id;
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
private Long sellerUserId;
|
||||
private String title;
|
||||
private List<String> imageUrls = new ArrayList<String>();
|
||||
private int priceCent;
|
||||
private String description;
|
||||
private String category;
|
||||
private String contactPhone;
|
||||
private String tradeMethod;
|
||||
private SecondGoodsStatus status;
|
||||
private String reviewReason;
|
||||
private List<SecondGoodsCommentResponse> comments = new ArrayList<SecondGoodsCommentResponse>();
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public void setCommunityId(Long communityId) {
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
public Long getSellerUserId() {
|
||||
return sellerUserId;
|
||||
}
|
||||
|
||||
public void setSellerUserId(Long sellerUserId) {
|
||||
this.sellerUserId = sellerUserId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public List<String> getImageUrls() {
|
||||
return imageUrls;
|
||||
}
|
||||
|
||||
public void setImageUrls(List<String> imageUrls) {
|
||||
this.imageUrls = imageUrls;
|
||||
}
|
||||
|
||||
public int getPriceCent() {
|
||||
return priceCent;
|
||||
}
|
||||
|
||||
public void setPriceCent(int priceCent) {
|
||||
this.priceCent = priceCent;
|
||||
}
|
||||
|
||||
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 String getContactPhone() {
|
||||
return contactPhone;
|
||||
}
|
||||
|
||||
public void setContactPhone(String contactPhone) {
|
||||
this.contactPhone = contactPhone;
|
||||
}
|
||||
|
||||
public String getTradeMethod() {
|
||||
return tradeMethod;
|
||||
}
|
||||
|
||||
public void setTradeMethod(String tradeMethod) {
|
||||
this.tradeMethod = tradeMethod;
|
||||
}
|
||||
|
||||
public SecondGoodsStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(SecondGoodsStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getReviewReason() {
|
||||
return reviewReason;
|
||||
}
|
||||
|
||||
public void setReviewReason(String reviewReason) {
|
||||
this.reviewReason = reviewReason;
|
||||
}
|
||||
|
||||
public List<SecondGoodsCommentResponse> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public void setComments(List<SecondGoodsCommentResponse> comments) {
|
||||
this.comments = comments;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package com.linhelp.secondhand;
|
||||
|
||||
import com.linhelp.common.enums.SecondGoodsStatus;
|
||||
import com.linhelp.common.exception.BizException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@Service
|
||||
public class SecondGoodsService {
|
||||
private final AtomicLong ids = new AtomicLong(1L);
|
||||
private final AtomicLong commentIds = new AtomicLong(1L);
|
||||
private final Map<Long, SecondGoodsResponse> goods = new LinkedHashMap<Long, SecondGoodsResponse>();
|
||||
|
||||
public synchronized SecondGoodsResponse create(Long sellerUserId, SecondGoodsRequest request) {
|
||||
SecondGoodsResponse response = new SecondGoodsResponse();
|
||||
response.setId(ids.getAndIncrement());
|
||||
response.setTenantId(request.getTenantId());
|
||||
response.setCommunityId(request.getCommunityId());
|
||||
response.setSellerUserId(sellerUserId);
|
||||
copyFields(request, response);
|
||||
response.setStatus(SecondGoodsStatus.PENDING_REVIEW);
|
||||
response.setCreatedAt(LocalDateTime.now());
|
||||
response.setUpdatedAt(response.getCreatedAt());
|
||||
goods.put(response.getId(), response);
|
||||
return copy(response);
|
||||
}
|
||||
|
||||
public synchronized List<SecondGoodsResponse> listPublic(Long communityId, String category) {
|
||||
List<SecondGoodsResponse> result = new ArrayList<SecondGoodsResponse>();
|
||||
for (SecondGoodsResponse item : goods.values()) {
|
||||
if (!communityId.equals(item.getCommunityId())) {
|
||||
continue;
|
||||
}
|
||||
if (category != null && !category.equals(item.getCategory())) {
|
||||
continue;
|
||||
}
|
||||
if (SecondGoodsStatus.PUBLISHED == item.getStatus()) {
|
||||
result.add(copy(item));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized List<SecondGoodsResponse> listAdmin(Long communityId, SecondGoodsStatus status) {
|
||||
List<SecondGoodsResponse> result = new ArrayList<SecondGoodsResponse>();
|
||||
for (SecondGoodsResponse item : goods.values()) {
|
||||
if (!communityId.equals(item.getCommunityId())) {
|
||||
continue;
|
||||
}
|
||||
if (status != null && status != item.getStatus()) {
|
||||
continue;
|
||||
}
|
||||
result.add(copy(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized SecondGoodsResponse detail(Long id) {
|
||||
return copy(require(id));
|
||||
}
|
||||
|
||||
public synchronized SecondGoodsResponse approve(Long id) {
|
||||
SecondGoodsResponse item = require(id);
|
||||
item.setStatus(SecondGoodsStatus.PUBLISHED);
|
||||
item.setReviewReason(null);
|
||||
item.setUpdatedAt(LocalDateTime.now());
|
||||
return copy(item);
|
||||
}
|
||||
|
||||
public synchronized SecondGoodsResponse reject(Long id, String reason) {
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
throw new BizException("拒绝原因不能为空");
|
||||
}
|
||||
SecondGoodsResponse item = require(id);
|
||||
item.setStatus(SecondGoodsStatus.REJECTED);
|
||||
item.setReviewReason(reason);
|
||||
item.setUpdatedAt(LocalDateTime.now());
|
||||
return copy(item);
|
||||
}
|
||||
|
||||
public synchronized SecondGoodsResponse adminOffShelf(Long id) {
|
||||
SecondGoodsResponse item = require(id);
|
||||
item.setStatus(SecondGoodsStatus.OFF_SHELF);
|
||||
item.setUpdatedAt(LocalDateTime.now());
|
||||
return copy(item);
|
||||
}
|
||||
|
||||
public synchronized SecondGoodsResponse offShelf(Long sellerUserId, Long id) {
|
||||
SecondGoodsResponse item = requireMine(sellerUserId, id);
|
||||
item.setStatus(SecondGoodsStatus.OFF_SHELF);
|
||||
item.setUpdatedAt(LocalDateTime.now());
|
||||
return copy(item);
|
||||
}
|
||||
|
||||
public synchronized SecondGoodsResponse sold(Long sellerUserId, Long id) {
|
||||
SecondGoodsResponse item = requireMine(sellerUserId, id);
|
||||
item.setStatus(SecondGoodsStatus.SOLD);
|
||||
item.setUpdatedAt(LocalDateTime.now());
|
||||
return copy(item);
|
||||
}
|
||||
|
||||
public synchronized SecondGoodsCommentResponse comment(Long userId, Long goodsId, SecondGoodsCommentRequest request) {
|
||||
SecondGoodsResponse item = require(goodsId);
|
||||
if (SecondGoodsStatus.PUBLISHED != item.getStatus()) {
|
||||
throw new BizException("闲置暂不可留言");
|
||||
}
|
||||
SecondGoodsCommentResponse comment = new SecondGoodsCommentResponse();
|
||||
comment.setId(commentIds.getAndIncrement());
|
||||
comment.setGoodsId(goodsId);
|
||||
comment.setUserId(userId);
|
||||
comment.setContent(request.getContent());
|
||||
comment.setCreatedAt(LocalDateTime.now());
|
||||
item.getComments().add(comment);
|
||||
return copyComment(comment);
|
||||
}
|
||||
|
||||
private SecondGoodsResponse requireMine(Long sellerUserId, Long id) {
|
||||
SecondGoodsResponse item = require(id);
|
||||
if (!sellerUserId.equals(item.getSellerUserId())) {
|
||||
throw new BizException(404, "闲置不存在");
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
private SecondGoodsResponse require(Long id) {
|
||||
SecondGoodsResponse item = goods.get(id);
|
||||
if (item == null) {
|
||||
throw new BizException(404, "闲置不存在");
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
private void copyFields(SecondGoodsRequest request, SecondGoodsResponse response) {
|
||||
response.setTitle(request.getTitle());
|
||||
response.setImageUrls(request.getImageUrls() == null
|
||||
? new ArrayList<String>()
|
||||
: new ArrayList<String>(request.getImageUrls()));
|
||||
response.setPriceCent(request.getPriceCent());
|
||||
response.setDescription(request.getDescription());
|
||||
response.setCategory(request.getCategory());
|
||||
response.setContactPhone(request.getContactPhone());
|
||||
response.setTradeMethod(request.getTradeMethod());
|
||||
}
|
||||
|
||||
private SecondGoodsResponse copy(SecondGoodsResponse source) {
|
||||
SecondGoodsResponse target = new SecondGoodsResponse();
|
||||
target.setId(source.getId());
|
||||
target.setTenantId(source.getTenantId());
|
||||
target.setCommunityId(source.getCommunityId());
|
||||
target.setSellerUserId(source.getSellerUserId());
|
||||
target.setTitle(source.getTitle());
|
||||
target.setImageUrls(new ArrayList<String>(source.getImageUrls()));
|
||||
target.setPriceCent(source.getPriceCent());
|
||||
target.setDescription(source.getDescription());
|
||||
target.setCategory(source.getCategory());
|
||||
target.setContactPhone(source.getContactPhone());
|
||||
target.setTradeMethod(source.getTradeMethod());
|
||||
target.setStatus(source.getStatus());
|
||||
target.setReviewReason(source.getReviewReason());
|
||||
target.setComments(copyComments(source.getComments()));
|
||||
target.setCreatedAt(source.getCreatedAt());
|
||||
target.setUpdatedAt(source.getUpdatedAt());
|
||||
return target;
|
||||
}
|
||||
|
||||
private List<SecondGoodsCommentResponse> copyComments(List<SecondGoodsCommentResponse> sourceComments) {
|
||||
List<SecondGoodsCommentResponse> comments = new ArrayList<SecondGoodsCommentResponse>();
|
||||
for (SecondGoodsCommentResponse source : sourceComments) {
|
||||
comments.add(copyComment(source));
|
||||
}
|
||||
return comments;
|
||||
}
|
||||
|
||||
private SecondGoodsCommentResponse copyComment(SecondGoodsCommentResponse source) {
|
||||
SecondGoodsCommentResponse target = new SecondGoodsCommentResponse();
|
||||
target.setId(source.getId());
|
||||
target.setGoodsId(source.getGoodsId());
|
||||
target.setUserId(source.getUserId());
|
||||
target.setContent(source.getContent());
|
||||
target.setCreatedAt(source.getCreatedAt());
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.linhelp.storage;
|
||||
|
||||
import com.linhelp.common.api.ApiResponse;
|
||||
import com.linhelp.common.security.AuthService;
|
||||
import com.linhelp.common.security.CurrentUser;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
public class FileStorageController {
|
||||
private final FileStorageService fileStorageService;
|
||||
private final AuthService authService;
|
||||
|
||||
public FileStorageController(FileStorageService fileStorageService, AuthService authService) {
|
||||
this.fileStorageService = fileStorageService;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping("/api/files/upload")
|
||||
public ApiResponse<FileUploadResponse> upload(@RequestParam("file") MultipartFile file) {
|
||||
CurrentUser user = authService.currentUser();
|
||||
return ApiResponse.ok(fileStorageService.upload(user.getTenantId(), user.getCommunityId(), file));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.linhelp.storage;
|
||||
|
||||
import com.linhelp.common.exception.BizException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class FileStorageService {
|
||||
private static final DateTimeFormatter DATE_PATH_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd");
|
||||
|
||||
public FileUploadResponse upload(Long tenantId, Long communityId, MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new BizException("文件不能为空");
|
||||
}
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || !contentType.startsWith("image/")) {
|
||||
throw new BizException("仅支持图片上传");
|
||||
}
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = extensionOf(originalFilename);
|
||||
String objectKey = "tenant/" + tenantId
|
||||
+ "/community/" + communityId
|
||||
+ "/" + DATE_PATH_FORMATTER.format(LocalDate.now())
|
||||
+ "/" + UUID.randomUUID().toString().replace("-", "")
|
||||
+ extension;
|
||||
|
||||
FileUploadResponse response = new FileUploadResponse();
|
||||
response.setOriginalFilename(originalFilename);
|
||||
response.setContentType(contentType);
|
||||
response.setSize(file.getSize());
|
||||
response.setObjectKey(objectKey);
|
||||
response.setUrl("/uploads/" + objectKey);
|
||||
return response;
|
||||
}
|
||||
|
||||
private String extensionOf(String filename) {
|
||||
if (filename == null) {
|
||||
return "";
|
||||
}
|
||||
int dot = filename.lastIndexOf('.');
|
||||
if (dot < 0 || dot == filename.length() - 1) {
|
||||
return "";
|
||||
}
|
||||
return filename.substring(dot).toLowerCase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.linhelp.storage;
|
||||
|
||||
public class FileUploadResponse {
|
||||
private String originalFilename;
|
||||
private String contentType;
|
||||
private long size;
|
||||
private String objectKey;
|
||||
private String url;
|
||||
|
||||
public String getOriginalFilename() {
|
||||
return originalFilename;
|
||||
}
|
||||
|
||||
public void setOriginalFilename(String originalFilename) {
|
||||
this.originalFilename = originalFilename;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String getObjectKey() {
|
||||
return objectKey;
|
||||
}
|
||||
|
||||
public void setObjectKey(String objectKey) {
|
||||
this.objectKey = objectKey;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.linhelp.feedback;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class FeedbackServiceTests {
|
||||
@Test
|
||||
void createFeedbackStartsPending() {
|
||||
FeedbackService service = new FeedbackService();
|
||||
FeedbackRequest request = new FeedbackRequest();
|
||||
request.setTenantId(1L);
|
||||
request.setCommunityId(1L);
|
||||
request.setCategory("DELIVERY");
|
||||
request.setContent("配送慢了");
|
||||
request.setContactPhone("13800000000");
|
||||
|
||||
FeedbackResponse response = service.create(100L, request);
|
||||
|
||||
assertThat(response.getUserId()).isEqualTo(100L);
|
||||
assertThat(response.getStatus()).isEqualTo(FeedbackService.STATUS_PENDING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.linhelp.groupbuy;
|
||||
|
||||
import com.linhelp.common.enums.DeliveryMethod;
|
||||
import com.linhelp.common.enums.GroupBuyOrderStatus;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class GroupBuyServiceTests {
|
||||
@Test
|
||||
void cannotOrderAfterGroupBuyDeadline() {
|
||||
GroupBuyService groupBuyService = new GroupBuyService();
|
||||
GroupBuyOrderService orderService = new GroupBuyOrderService(groupBuyService);
|
||||
GroupBuyResponse groupBuy = groupBuyService.create(groupBuyRequest(LocalDateTime.now().minusMinutes(1)));
|
||||
groupBuyService.start(groupBuy.getId());
|
||||
|
||||
assertThatThrownBy(() -> orderService.create(1L, orderRequest(groupBuy.getId(), DeliveryMethod.IMMEDIATE)))
|
||||
.hasMessageContaining("团购已截单");
|
||||
}
|
||||
|
||||
@Test
|
||||
void groupBuyOrderCanBePreparedForSelfPickup() {
|
||||
GroupBuyService groupBuyService = new GroupBuyService();
|
||||
GroupBuyOrderService orderService = new GroupBuyOrderService(groupBuyService);
|
||||
GroupBuyResponse groupBuy = groupBuyService.create(groupBuyRequest(LocalDateTime.now().plusDays(1)));
|
||||
groupBuyService.start(groupBuy.getId());
|
||||
|
||||
GroupBuyOrderResponse order = orderService.create(1L, orderRequest(groupBuy.getId(), DeliveryMethod.SELF_PICKUP));
|
||||
orderService.confirm(order.getId());
|
||||
orderService.markReady(order.getId());
|
||||
|
||||
assertThat(orderService.detail(order.getId()).getStatus()).isEqualTo(GroupBuyOrderStatus.PENDING_PICKUP);
|
||||
}
|
||||
|
||||
private GroupBuyRequest groupBuyRequest(LocalDateTime endTime) {
|
||||
GroupBuyRequest request = new GroupBuyRequest();
|
||||
request.setTenantId(1L);
|
||||
request.setCommunityId(1L);
|
||||
request.setTitle("今日鸡蛋团购");
|
||||
request.setCoverUrl("https://img.test/egg.jpg");
|
||||
request.setPriceCent(2990);
|
||||
request.setStock(100);
|
||||
request.setStartTime(LocalDateTime.now().minusHours(1));
|
||||
request.setEndTime(endTime);
|
||||
request.setPickupAddress("小区北门");
|
||||
return request;
|
||||
}
|
||||
|
||||
private GroupBuyOrderRequest orderRequest(Long groupBuyId, DeliveryMethod deliveryMethod) {
|
||||
GroupBuyOrderRequest request = new GroupBuyOrderRequest();
|
||||
request.setTenantId(1L);
|
||||
request.setCommunityId(1L);
|
||||
request.setGroupBuyId(groupBuyId);
|
||||
request.setAddressId(1L);
|
||||
request.setQuantity(2);
|
||||
request.setDeliveryMethod(deliveryMethod);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.linhelp.secondhand;
|
||||
|
||||
import com.linhelp.common.enums.SecondGoodsStatus;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class SecondGoodsReviewTests {
|
||||
@Test
|
||||
void newListingStartsPendingAndBecomesVisibleAfterApproval() {
|
||||
SecondGoodsService service = new SecondGoodsService();
|
||||
SecondGoodsResponse listing = service.create(1L, request());
|
||||
|
||||
assertThat(listing.getStatus()).isEqualTo(SecondGoodsStatus.PENDING_REVIEW);
|
||||
assertThat(service.listPublic(1L, null)).isEmpty();
|
||||
|
||||
service.approve(listing.getId());
|
||||
|
||||
assertThat(service.listPublic(1L, null)).extracting(SecondGoodsResponse::getId)
|
||||
.containsExactly(listing.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectionRequiresReason() {
|
||||
SecondGoodsService service = new SecondGoodsService();
|
||||
SecondGoodsResponse listing = service.create(1L, request());
|
||||
|
||||
assertThatThrownBy(() -> service.reject(listing.getId(), ""))
|
||||
.hasMessageContaining("拒绝原因不能为空");
|
||||
}
|
||||
|
||||
private SecondGoodsRequest request() {
|
||||
SecondGoodsRequest request = new SecondGoodsRequest();
|
||||
request.setTenantId(1L);
|
||||
request.setCommunityId(1L);
|
||||
request.setTitle("九成新儿童车");
|
||||
request.setPriceCent(9900);
|
||||
request.setDescription("小区内自提");
|
||||
request.setCategory("母婴");
|
||||
request.setContactPhone("13800000000");
|
||||
request.setTradeMethod("FACE_TO_FACE");
|
||||
request.setImageUrls(Arrays.asList("https://img.test/bike.jpg"));
|
||||
return request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.linhelp.storage;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class FileStorageServiceTests {
|
||||
@Test
|
||||
void imageUploadUsesTenantCommunityDatePrefix() {
|
||||
FileStorageService service = new FileStorageService();
|
||||
MockMultipartFile file = new MockMultipartFile("file", "avatar.png", "image/png", new byte[]{1, 2, 3});
|
||||
|
||||
FileUploadResponse response = service.upload(1L, 2L, file);
|
||||
|
||||
assertThat(response.getObjectKey()).startsWith("tenant/1/community/2/");
|
||||
assertThat(response.getObjectKey()).endsWith(".png");
|
||||
assertThat(response.getUrl()).contains(response.getObjectKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonImageUploadIsRejected() {
|
||||
FileStorageService service = new FileStorageService();
|
||||
MockMultipartFile file = new MockMultipartFile("file", "doc.pdf", "application/pdf", new byte[]{1});
|
||||
|
||||
assertThatThrownBy(() -> service.upload(1L, 1L, file))
|
||||
.hasMessageContaining("仅支持图片上传");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user