9.18/2
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
package com.example.demo.customer.controller;
|
||||
|
||||
import com.example.demo.common.AppDefaultsProperties;
|
||||
import com.example.demo.customer.dto.CustomerDtos;
|
||||
import com.example.demo.customer.entity.Customer;
|
||||
import com.example.demo.customer.service.CustomerService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/customers")
|
||||
public class CustomerController {
|
||||
|
||||
private final CustomerService customerService;
|
||||
private final AppDefaultsProperties defaults;
|
||||
|
||||
public CustomerController(CustomerService customerService, AppDefaultsProperties defaults) {
|
||||
this.customerService = customerService;
|
||||
this.defaults = defaults;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<?> search(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "debtOnly", required = false, defaultValue = "false") boolean debtOnly,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "50") int size) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(customerService.search(sid, kw, debtOnly, Math.max(0, page-1), size));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<?> detail(@PathVariable("id") Long id,
|
||||
@RequestHeader(name = "X-Shop-Id", required = false) Long shopId) {
|
||||
java.util.Optional<Customer> oc = customerService.findById(id);
|
||||
if (oc.isEmpty()) return ResponseEntity.notFound().build();
|
||||
Customer c = oc.get();
|
||||
java.util.Map<String,Object> body = new java.util.HashMap<>();
|
||||
body.put("id", c.getId());
|
||||
body.put("name", c.getName());
|
||||
body.put("contactName", c.getContactName());
|
||||
body.put("mobile", c.getMobile());
|
||||
body.put("phone", c.getPhone());
|
||||
body.put("level", c.getLevel());
|
||||
body.put("priceLevel", c.getPriceLevel());
|
||||
body.put("remark", c.getRemark());
|
||||
body.put("address", c.getAddress());
|
||||
body.put("receivable", new java.math.BigDecimal("0")); // 详情页如需可扩展计算
|
||||
return ResponseEntity.ok(body);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> create(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||
@RequestBody CustomerDtos.CreateOrUpdateCustomerRequest req) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||
Long id = customerService.create(sid, uid, req);
|
||||
java.util.Map<String,Object> body = new java.util.HashMap<>(); body.put("id", id); return ResponseEntity.ok(body);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<?> update(@PathVariable("id") Long id,
|
||||
@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||
@RequestBody CustomerDtos.CreateOrUpdateCustomerRequest req) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||
customerService.update(id, sid, uid, req);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.example.demo.customer.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class CustomerDtos {
|
||||
|
||||
public static class CustomerListItem {
|
||||
public Long id;
|
||||
public String name;
|
||||
public String contactName;
|
||||
public String mobile;
|
||||
public String phone;
|
||||
public String level;
|
||||
public String priceLevel;
|
||||
public String remark;
|
||||
public BigDecimal receivable;
|
||||
public CustomerListItem() {}
|
||||
public CustomerListItem(Long id, String name, String contactName, String mobile, String phone, String level, String priceLevel, String remark, BigDecimal receivable) {
|
||||
this.id = id; this.name = name; this.contactName = contactName; this.mobile = mobile; this.phone = phone; this.level = level; this.priceLevel = priceLevel; this.remark = remark; this.receivable = receivable;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CreateOrUpdateCustomerRequest {
|
||||
public String name;
|
||||
public String level;
|
||||
public String priceLevel;
|
||||
public String contactName;
|
||||
public String mobile;
|
||||
public String phone;
|
||||
public String address;
|
||||
public java.math.BigDecimal arOpening;
|
||||
public String remark;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.example.demo.customer.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "customers")
|
||||
public class Customer {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "shop_id", nullable = false)
|
||||
private Long shopId;
|
||||
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private Long userId;
|
||||
|
||||
@Column(name = "name", nullable = false, length = 120)
|
||||
private String name;
|
||||
|
||||
@Column(name = "phone", length = 32)
|
||||
private String phone;
|
||||
|
||||
@Column(name = "mobile", length = 32)
|
||||
private String mobile;
|
||||
|
||||
@Column(name = "address", length = 255)
|
||||
private String address;
|
||||
|
||||
@Column(name = "level", length = 32)
|
||||
private String level;
|
||||
|
||||
@Column(name = "contact_name", length = 64)
|
||||
private String contactName;
|
||||
|
||||
@Column(name = "price_level", nullable = false, length = 32)
|
||||
private String priceLevel;
|
||||
|
||||
@Column(name = "status", nullable = false)
|
||||
private Integer status;
|
||||
|
||||
@Column(name = "ar_opening", precision = 18, scale = 2, nullable = false)
|
||||
private BigDecimal arOpening;
|
||||
|
||||
@Column(name = "remark", length = 255)
|
||||
private String remark;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Column(name = "deleted_at")
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public Long getShopId() { return shopId; }
|
||||
public void setShopId(Long shopId) { this.shopId = shopId; }
|
||||
public Long getUserId() { return userId; }
|
||||
public void setUserId(Long userId) { this.userId = userId; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getPhone() { return phone; }
|
||||
public void setPhone(String phone) { this.phone = phone; }
|
||||
public String getMobile() { return mobile; }
|
||||
public void setMobile(String mobile) { this.mobile = mobile; }
|
||||
public String getAddress() { return address; }
|
||||
public void setAddress(String address) { this.address = address; }
|
||||
public String getLevel() { return level; }
|
||||
public void setLevel(String level) { this.level = level; }
|
||||
public String getContactName() { return contactName; }
|
||||
public void setContactName(String contactName) { this.contactName = contactName; }
|
||||
public String getPriceLevel() { return priceLevel; }
|
||||
public void setPriceLevel(String priceLevel) { this.priceLevel = priceLevel; }
|
||||
public Integer getStatus() { return status; }
|
||||
public void setStatus(Integer status) { this.status = status; }
|
||||
public BigDecimal getArOpening() { return arOpening; }
|
||||
public void setArOpening(BigDecimal arOpening) { this.arOpening = arOpening; }
|
||||
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; }
|
||||
public LocalDateTime getDeletedAt() { return deletedAt; }
|
||||
public void setDeletedAt(LocalDateTime deletedAt) { this.deletedAt = deletedAt; }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.example.demo.customer.repo;
|
||||
|
||||
import com.example.demo.customer.entity.Customer;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CustomerRepository extends JpaRepository<Customer, Long> {
|
||||
|
||||
@Query("SELECT c FROM Customer c WHERE c.shopId=:shopId AND c.deletedAt IS NULL AND ( :kw IS NULL OR c.name LIKE CONCAT('%',:kw,'%') OR c.mobile LIKE CONCAT('%',:kw,'%') OR c.phone LIKE CONCAT('%',:kw,'%')) ORDER BY c.id DESC")
|
||||
List<Customer> search(@Param("shopId") Long shopId, @Param("kw") String kw, org.springframework.data.domain.Pageable pageable);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.example.demo.customer.service;
|
||||
|
||||
import com.example.demo.customer.dto.CustomerDtos;
|
||||
import com.example.demo.customer.entity.Customer;
|
||||
import com.example.demo.customer.repo.CustomerRepository;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class CustomerService {
|
||||
|
||||
private final CustomerRepository customerRepository;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public CustomerService(CustomerRepository customerRepository, JdbcTemplate jdbcTemplate) {
|
||||
this.customerRepository = customerRepository;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
public java.util.Map<String, Object> search(Long shopId, String kw, boolean debtOnly, int page, int size) {
|
||||
List<Customer> list = customerRepository.search(shopId, kw, PageRequest.of(page, size));
|
||||
List<CustomerDtos.CustomerListItem> items = list.stream().map(c -> new CustomerDtos.CustomerListItem(
|
||||
c.getId(), c.getName(), c.getContactName(), c.getMobile(), c.getPhone(), c.getLevel(), c.getPriceLevel(), c.getRemark(), calcReceivable(shopId, c.getId(), c.getArOpening())
|
||||
)).collect(Collectors.toList());
|
||||
if (debtOnly) {
|
||||
items = items.stream().filter(it -> it.receivable != null && it.receivable.compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
|
||||
}
|
||||
java.util.Map<String, Object> resp = new java.util.HashMap<>();
|
||||
resp.put("list", items);
|
||||
return resp;
|
||||
}
|
||||
|
||||
public Optional<Customer> findById(Long id) {
|
||||
return customerRepository.findById(id);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Long create(Long shopId, Long userId, CustomerDtos.CreateOrUpdateCustomerRequest req) {
|
||||
Customer c = new Customer();
|
||||
c.setShopId(shopId); c.setUserId(userId);
|
||||
c.setName(req.name); c.setLevel(req.level); c.setPriceLevel(nullToDefaultPriceLevel(req.priceLevel));
|
||||
c.setContactName(req.contactName); c.setMobile(req.mobile); c.setPhone(req.phone); c.setAddress(req.address);
|
||||
c.setStatus(1);
|
||||
c.setArOpening(req.arOpening == null ? BigDecimal.ZERO : req.arOpening);
|
||||
c.setRemark(req.remark);
|
||||
java.time.LocalDateTime now = java.time.LocalDateTime.now();
|
||||
c.setCreatedAt(now);
|
||||
c.setUpdatedAt(now);
|
||||
return customerRepository.save(c).getId();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void update(Long id, Long shopId, Long userId, CustomerDtos.CreateOrUpdateCustomerRequest req) {
|
||||
Customer c = customerRepository.findById(id).orElseThrow();
|
||||
if (!c.getShopId().equals(shopId)) throw new IllegalArgumentException("跨店铺修改");
|
||||
c.setName(req.name); c.setLevel(req.level); c.setPriceLevel(nullToDefaultPriceLevel(req.priceLevel));
|
||||
c.setContactName(req.contactName); c.setMobile(req.mobile); c.setPhone(req.phone); c.setAddress(req.address);
|
||||
if (req.arOpening != null) c.setArOpening(req.arOpening);
|
||||
c.setRemark(req.remark);
|
||||
c.setUpdatedAt(java.time.LocalDateTime.now());
|
||||
customerRepository.save(c);
|
||||
}
|
||||
|
||||
private String nullToDefaultPriceLevel(String v) { return (v == null || v.isBlank()) ? "retail" : v; }
|
||||
|
||||
private BigDecimal calcReceivable(Long shopId, Long customerId, BigDecimal opening) {
|
||||
BigDecimal open = opening == null ? BigDecimal.ZERO : opening;
|
||||
BigDecimal sale = n(jdbcTemplate.queryForObject("SELECT COALESCE(SUM(amount - paid_amount),0) FROM sales_orders WHERE shop_id=? AND customer_id=? AND status='approved'", BigDecimal.class, shopId, customerId));
|
||||
BigDecimal saleRet = n(jdbcTemplate.queryForObject("SELECT COALESCE(SUM(amount - paid_amount),0) FROM sales_return_orders WHERE shop_id=? AND customer_id=? AND status='approved'", BigDecimal.class, shopId, customerId));
|
||||
BigDecimal otherIn = n(jdbcTemplate.queryForObject("SELECT COALESCE(SUM(amount),0) FROM other_transactions WHERE shop_id=? AND counterparty_type='customer' AND counterparty_id=? AND `type`='income'", BigDecimal.class, shopId, customerId));
|
||||
BigDecimal otherOut = n(jdbcTemplate.queryForObject("SELECT COALESCE(SUM(amount),0) FROM other_transactions WHERE shop_id=? AND counterparty_type='customer' AND counterparty_id=? AND `type`='expense'", BigDecimal.class, shopId, customerId));
|
||||
return open.add(sale).subtract(saleRet).add(otherIn).subtract(otherOut).setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private static BigDecimal n(BigDecimal v) { return v == null ? BigDecimal.ZERO : v; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user