9.18王德鹏/1
This commit is contained in:
@@ -71,3 +71,6 @@ public class AttachmentController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -30,3 +30,6 @@ public class AttachmentPlaceholderProperties {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,3 +19,6 @@ public class AppDefaultsProperties {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.example.demo.order;
|
||||
|
||||
import com.example.demo.common.AppDefaultsProperties;
|
||||
import com.example.demo.order.dto.OrderDtos;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class OrderController {
|
||||
|
||||
private final OrderService orderService;
|
||||
private final AppDefaultsProperties defaults;
|
||||
|
||||
public OrderController(OrderService orderService, AppDefaultsProperties defaults) {
|
||||
this.orderService = orderService;
|
||||
this.defaults = defaults;
|
||||
}
|
||||
|
||||
@PostMapping("/orders")
|
||||
public ResponseEntity<?> createOrder(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||
@RequestBody OrderDtos.CreateOrderRequest req) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||
return ResponseEntity.ok(orderService.create(sid, uid, req));
|
||||
}
|
||||
|
||||
@PostMapping("/payments/{biz}")
|
||||
public ResponseEntity<?> createPayments(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||
@PathVariable("biz") String biz,
|
||||
@RequestBody java.util.List<OrderDtos.PaymentItem> req) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||
return ResponseEntity.ok(orderService.createPayments(sid, uid, req, biz));
|
||||
}
|
||||
|
||||
@PostMapping("/orders/{id}/void")
|
||||
public ResponseEntity<?> voidOrder(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestHeader(name = "X-User-Id", required = false) Long userId,
|
||||
@PathVariable("id") Long id,
|
||||
@RequestParam("type") String type) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
Long uid = (userId == null ? defaults.getUserId() : userId);
|
||||
orderService.voidOrder(sid, uid, id, type);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@GetMapping("/orders")
|
||||
public ResponseEntity<?> list(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "biz", required = false) String biz,
|
||||
@RequestParam(name = "type", required = false) String type,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(orderService.list(sid, biz, type, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
|
||||
// 兼容前端直接调用 /api/purchase-orders
|
||||
@GetMapping("/purchase-orders")
|
||||
public ResponseEntity<?> purchaseOrders(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "status", required = false) String status,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
String type = ("returned".equalsIgnoreCase(status) ? "purchase.return" : "purchase.in");
|
||||
return ResponseEntity.ok(orderService.list(sid, "purchase", type, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
|
||||
@GetMapping("/payments")
|
||||
public ResponseEntity<?> listPayments(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "direction", required = false) String direction,
|
||||
@RequestParam(name = "bizType", required = false) String bizType,
|
||||
@RequestParam(name = "accountId", required = false) Long accountId,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(orderService.listPayments(sid, direction, bizType, accountId, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
|
||||
@GetMapping("/other-transactions")
|
||||
public ResponseEntity<?> listOtherTransactions(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "type", required = false) String type,
|
||||
@RequestParam(name = "accountId", required = false) Long accountId,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(orderService.listOtherTransactions(sid, type, accountId, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
|
||||
@GetMapping("/inventories/logs")
|
||||
public ResponseEntity<?> listInventoryLogs(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId,
|
||||
@RequestParam(name = "productId", required = false) Long productId,
|
||||
@RequestParam(name = "reason", required = false) String reason,
|
||||
@RequestParam(name = "kw", required = false) String kw,
|
||||
@RequestParam(name = "page", defaultValue = "1") int page,
|
||||
@RequestParam(name = "size", defaultValue = "20") int size,
|
||||
@RequestParam(name = "startDate", required = false) String startDate,
|
||||
@RequestParam(name = "endDate", required = false) String endDate) {
|
||||
Long sid = (shopId == null ? defaults.getShopId() : shopId);
|
||||
return ResponseEntity.ok(orderService.listInventoryLogs(sid, productId, reason, kw, Math.max(0, page-1), size, startDate, endDate));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.example.demo.order;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 简单的进程内单号生成器:前缀 + yyyyMMdd + 4位流水
|
||||
* 说明:演示用,生产建议使用数据库序列或 Redis 自增确保多实例唯一。
|
||||
*/
|
||||
public class OrderNumberGenerator {
|
||||
private static final ConcurrentHashMap<String, AtomicInteger> dateCounters = new ConcurrentHashMap<>();
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
public static String next(String prefix) {
|
||||
String day = LocalDateTime.now().format(DATE);
|
||||
String key = prefix + day;
|
||||
int seq = dateCounters.computeIfAbsent(key, k -> new AtomicInteger(0)).incrementAndGet();
|
||||
return prefix + day + String.format("%04d", seq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
353
backend/src/main/java/com/example/demo/order/OrderService.java
Normal file
353
backend/src/main/java/com/example/demo/order/OrderService.java
Normal file
@@ -0,0 +1,353 @@
|
||||
package com.example.demo.order;
|
||||
|
||||
import com.example.demo.order.dto.OrderDtos;
|
||||
import com.example.demo.product.entity.Inventory;
|
||||
import com.example.demo.product.repo.InventoryRepository;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
import org.springframework.jdbc.support.KeyHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class OrderService {
|
||||
|
||||
private final InventoryRepository inventoryRepository;
|
||||
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public OrderService(InventoryRepository inventoryRepository,
|
||||
JdbcTemplate jdbcTemplate) {
|
||||
this.inventoryRepository = inventoryRepository;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Object create(Long shopId, Long userId, OrderDtos.CreateOrderRequest req) {
|
||||
String type = req.type == null ? "" : req.type;
|
||||
boolean isSaleOut = "sale.out".equals(type) || "out".equals(type) || "sale".equals(type);
|
||||
boolean isPurchaseIn = "purchase.in".equals(type) || "in".equals(type);
|
||||
boolean isSaleReturn = "sale.return".equals(type);
|
||||
boolean isPurchaseReturn = "purchase.return".equals(type);
|
||||
boolean isSaleCollect = "sale.collect".equals(type);
|
||||
boolean isPurchasePay = "purchase.pay".equals(type);
|
||||
|
||||
if (isSaleCollect || isPurchasePay) {
|
||||
java.util.List<OrderDtos.PaymentItem> payments = req.payments == null ? java.util.List.of() : req.payments;
|
||||
return createPayments(shopId, userId, payments, isSaleCollect ? "sale" : "purchase");
|
||||
}
|
||||
|
||||
if (!(isSaleOut || isPurchaseIn || isSaleReturn || isPurchaseReturn)) throw new IllegalArgumentException("不支持的type");
|
||||
if (req.items == null || req.items.isEmpty()) throw new IllegalArgumentException("明细为空");
|
||||
|
||||
// 后端重算金额
|
||||
final BigDecimal[] totalRef = new BigDecimal[]{BigDecimal.ZERO};
|
||||
for (OrderDtos.Item it : req.items) {
|
||||
BigDecimal qty = n(it.quantity);
|
||||
BigDecimal price = n(it.unitPrice);
|
||||
BigDecimal dr = n(it.discountRate);
|
||||
BigDecimal line = qty.multiply(price).multiply(BigDecimal.ONE.subtract(dr.divide(new BigDecimal("100"))));
|
||||
totalRef[0] = totalRef[0].add(scale2(line));
|
||||
}
|
||||
|
||||
// 库存变动(保存即 approved)
|
||||
LocalDateTime now = nowUtc();
|
||||
for (OrderDtos.Item it : req.items) {
|
||||
Long pid = it.productId;
|
||||
Inventory inv = inventoryRepository.findById(pid).orElseGet(Inventory::new);
|
||||
inv.setProductId(pid);
|
||||
inv.setShopId(shopId);
|
||||
inv.setUserId(userId);
|
||||
BigDecimal cur = n(inv.getQuantity());
|
||||
BigDecimal delta = BigDecimal.ZERO;
|
||||
if (isSaleOut) delta = n(it.quantity).negate();
|
||||
if (isPurchaseIn) delta = n(it.quantity);
|
||||
if (isSaleReturn) delta = n(it.quantity); // 退货入库
|
||||
if (isPurchaseReturn) delta = n(it.quantity).negate(); // 退货出库
|
||||
BigDecimal next = cur.add(delta);
|
||||
if (isSaleOut && next.compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new IllegalStateException("库存不足");
|
||||
}
|
||||
inv.setQuantity(next);
|
||||
inv.setUpdatedAt(now);
|
||||
inventoryRepository.save(inv);
|
||||
|
||||
// 写入库存流水(可选金额)
|
||||
String imSql = "INSERT INTO inventory_movements (shop_id,user_id,product_id,source_type,source_id,qty_delta,amount_delta,reason,tx_time,remark,created_at) VALUES (?,?,?,?,?,?,?,?,?,NULL,NOW())";
|
||||
String sourceType = isSaleOut ? "sale" : (isPurchaseIn ? "purchase" : (isSaleReturn ? "sale_return" : "purchase_return"));
|
||||
jdbcTemplate.update(imSql, shopId, userId, pid, sourceType, null, delta, null, null, java.sql.Timestamp.from(now.atZone(java.time.ZoneOffset.UTC).toInstant()));
|
||||
}
|
||||
|
||||
String prefix = isSaleOut || isSaleReturn ? (isSaleReturn ? "SR" : "SO") : (isPurchaseReturn ? "PR" : "PO");
|
||||
String orderNo = OrderNumberGenerator.next(prefix);
|
||||
|
||||
// 持久化订单头与明细(简化使用 JDBC)
|
||||
String headTable = isSaleOut ? "sales_orders" : (isPurchaseIn ? "purchase_orders" : (isSaleReturn ? "sales_return_orders" : "purchase_return_orders"));
|
||||
String itemTable = isSaleOut ? "sales_order_items" : (isPurchaseIn ? "purchase_order_items" : (isSaleReturn ? "sales_return_order_items" : "purchase_return_order_items"));
|
||||
|
||||
// insert head
|
||||
KeyHolder kh = new GeneratedKeyHolder();
|
||||
String headSql = "INSERT INTO " + headTable + " (shop_id,user_id,customer_id,supplier_id,order_no,order_time,status,amount,paid_amount,remark,created_at,updated_at) " +
|
||||
"VALUES (?,?,?,?,?,?, 'approved', ?, 0, ?, NOW(), NOW())";
|
||||
Long customerId = req.customerId;
|
||||
Long supplierId = req.supplierId;
|
||||
jdbcTemplate.update(con -> {
|
||||
java.sql.PreparedStatement ps = con.prepareStatement(headSql, new String[]{"id"});
|
||||
ps.setLong(1, shopId);
|
||||
ps.setLong(2, userId);
|
||||
if (headTable.startsWith("sales")) {
|
||||
ps.setObject(3, customerId, java.sql.Types.BIGINT);
|
||||
ps.setObject(4, null);
|
||||
} else if (headTable.startsWith("purchase")) {
|
||||
ps.setObject(3, null);
|
||||
ps.setObject(4, supplierId, java.sql.Types.BIGINT);
|
||||
} else {
|
||||
ps.setObject(3, null);
|
||||
ps.setObject(4, null);
|
||||
}
|
||||
ps.setString(5, orderNo);
|
||||
ps.setTimestamp(6, java.sql.Timestamp.from(now.atZone(java.time.ZoneOffset.UTC).toInstant()));
|
||||
ps.setBigDecimal(7, scale2(totalRef[0]));
|
||||
ps.setString(8, req.remark);
|
||||
return ps;
|
||||
}, kh);
|
||||
Number orderKey = kh.getKey();
|
||||
Long orderId = (orderKey == null ? null : orderKey.longValue());
|
||||
|
||||
// insert items
|
||||
String itemSql = "INSERT INTO " + itemTable + " (order_id,product_id,quantity,unit_price,discount_rate,amount) VALUES (?,?,?,?,?,?)";
|
||||
for (OrderDtos.Item it : req.items) {
|
||||
BigDecimal qty = n(it.quantity);
|
||||
BigDecimal price = n(it.unitPrice);
|
||||
BigDecimal dr = n(it.discountRate);
|
||||
BigDecimal line = scale2(qty.multiply(price).multiply(BigDecimal.ONE.subtract(dr.divide(new BigDecimal("100")))));
|
||||
jdbcTemplate.update(itemSql, orderId, it.productId, qty, price, dr, line);
|
||||
}
|
||||
|
||||
return new OrderDtos.CreateOrderResponse(orderId, orderNo);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OrderDtos.CreatePaymentsResponse createPayments(Long shopId, Long userId, java.util.List<OrderDtos.PaymentItem> req, String bizType) {
|
||||
ensureDefaultAccounts(shopId, userId);
|
||||
List<Long> ids = new ArrayList<>();
|
||||
if (req == null) return new OrderDtos.CreatePaymentsResponse(ids);
|
||||
String direction = "sale".equals(bizType) ? "in" : "out";
|
||||
for (OrderDtos.PaymentItem p : req) {
|
||||
Long accountId = resolveAccountId(shopId, userId, p.method);
|
||||
KeyHolder kh = new GeneratedKeyHolder();
|
||||
String sql = "INSERT INTO payments (shop_id,user_id,biz_type,biz_id,account_id,direction,amount,pay_time,remark,created_at) " +
|
||||
"VALUES (?,?,?,?,?,?,?,NOW(),NULL,NOW())";
|
||||
jdbcTemplate.update(con -> {
|
||||
java.sql.PreparedStatement ps = con.prepareStatement(sql, new String[]{"id"});
|
||||
ps.setLong(1, shopId);
|
||||
ps.setLong(2, userId);
|
||||
ps.setString(3, bizType);
|
||||
if (p.orderId == null) ps.setNull(4, java.sql.Types.BIGINT); else ps.setLong(4, p.orderId);
|
||||
ps.setLong(5, accountId);
|
||||
ps.setString(6, direction);
|
||||
ps.setBigDecimal(7, n(p.amount));
|
||||
return ps;
|
||||
}, kh);
|
||||
Number payKey = kh.getKey();
|
||||
Long pid = (payKey == null ? null : payKey.longValue());
|
||||
if (pid != null) ids.add(pid);
|
||||
|
||||
// 若挂单,累加已付
|
||||
if (p.orderId != null) {
|
||||
String table = "sale".equals(bizType) ? "sales_orders" : "purchase_orders";
|
||||
jdbcTemplate.update("UPDATE " + table + " SET paid_amount = paid_amount + ? WHERE id = ?", n(p.amount), p.orderId);
|
||||
}
|
||||
}
|
||||
return new OrderDtos.CreatePaymentsResponse(ids);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void voidOrder(Long shopId, Long userId, Long id, String type) {
|
||||
// type: sale.out / purchase.in / sale.return / purchase.return
|
||||
String headTable;
|
||||
String itemTable;
|
||||
boolean revertIncrease; // true 表示作废时库存应减少(原先增加),false 表示应增加(原先减少)
|
||||
if ("sale.out".equals(type)) { headTable = "sales_orders"; itemTable = "sales_order_items"; revertIncrease = false; }
|
||||
else if ("purchase.in".equals(type)) { headTable = "purchase_orders"; itemTable = "purchase_order_items"; revertIncrease = true; }
|
||||
else if ("sale.return".equals(type)) { headTable = "sales_return_orders"; itemTable = "sales_return_order_items"; revertIncrease = true; }
|
||||
else if ("purchase.return".equals(type)) { headTable = "purchase_return_orders"; itemTable = "purchase_return_order_items"; revertIncrease = false; }
|
||||
else throw new IllegalArgumentException("不支持的type");
|
||||
|
||||
// 查询明细
|
||||
List<java.util.Map<String,Object>> rows = jdbcTemplate.queryForList("SELECT product_id, quantity FROM " + itemTable + " WHERE order_id = ?", id);
|
||||
// 回滚库存
|
||||
LocalDateTime now = nowUtc();
|
||||
for (java.util.Map<String,Object> r : rows) {
|
||||
Long pid = ((Number)r.get("product_id")).longValue();
|
||||
java.math.BigDecimal qty = new java.math.BigDecimal(r.get("quantity").toString());
|
||||
Inventory inv = inventoryRepository.findById(pid).orElseGet(Inventory::new);
|
||||
inv.setProductId(pid);
|
||||
inv.setShopId(shopId);
|
||||
inv.setUserId(userId);
|
||||
java.math.BigDecimal delta = revertIncrease ? qty.negate() : qty; // 与创建时相反
|
||||
inv.setQuantity(n(inv.getQuantity()).add(delta));
|
||||
inv.setUpdatedAt(now);
|
||||
inventoryRepository.save(inv);
|
||||
}
|
||||
// 更新状态
|
||||
jdbcTemplate.update("UPDATE " + headTable + " SET status='void' WHERE id = ?", id);
|
||||
}
|
||||
|
||||
public java.util.Map<String,Object> list(Long shopId, String biz, String type, String kw, int page, int size, String startDate, String endDate) {
|
||||
String headTable;
|
||||
if ("sale".equals(biz)) {
|
||||
headTable = ("sale.return".equals(type) ? "sales_return_orders" : "sales_orders");
|
||||
} else if ("purchase".equals(biz)) {
|
||||
headTable = ("purchase.return".equals(type) ? "purchase_orders" : "purchase_orders");
|
||||
} else {
|
||||
// 若未传,默认销售出货
|
||||
headTable = "sales_orders";
|
||||
}
|
||||
StringBuilder sql = new StringBuilder("SELECT id, order_no, order_time, amount FROM " + headTable + " WHERE shop_id=?");
|
||||
java.util.List<Object> ps = new java.util.ArrayList<>();
|
||||
ps.add(shopId);
|
||||
if ("purchase".equals(biz) && "purchase.return".equals(type)) {
|
||||
sql.append(" AND status='returned'");
|
||||
}
|
||||
if (kw != null && !kw.isBlank()) {
|
||||
sql.append(" AND (order_no LIKE ?)");
|
||||
ps.add('%' + kw + '%');
|
||||
}
|
||||
if (startDate != null && !startDate.isBlank()) { sql.append(" AND order_time>=?"); ps.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sql.append(" AND order_time<=?"); ps.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
sql.append(" ORDER BY order_time DESC LIMIT ? OFFSET ?");
|
||||
ps.add(size);
|
||||
ps.add(page * size);
|
||||
java.util.List<java.util.Map<String,Object>> list = jdbcTemplate.queryForList(sql.toString(), ps.toArray());
|
||||
// 汇总
|
||||
StringBuilder sumSql = new StringBuilder("SELECT COALESCE(SUM(amount),0) FROM " + headTable + " WHERE shop_id=?");
|
||||
java.util.List<Object> sumPs = new java.util.ArrayList<>();
|
||||
sumPs.add(shopId);
|
||||
if ("purchase".equals(biz) && "purchase.return".equals(type)) { sumSql.append(" AND status='returned'"); }
|
||||
if (kw != null && !kw.isBlank()) { sumSql.append(" AND (order_no LIKE ?)"); sumPs.add('%' + kw + '%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sumSql.append(" AND order_time>=?"); sumPs.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sumSql.append(" AND order_time<=?"); sumPs.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
java.math.BigDecimal total = jdbcTemplate.queryForObject(sumSql.toString(), java.math.BigDecimal.class, sumPs.toArray());
|
||||
java.util.Map<String,Object> resp = new java.util.HashMap<>();
|
||||
resp.put("list", list);
|
||||
resp.put("totalAmount", total == null ? java.math.BigDecimal.ZERO : total);
|
||||
return resp;
|
||||
}
|
||||
|
||||
public java.util.Map<String,Object> listPayments(Long shopId, String direction, String bizType, Long accountId, String kw, int page, int size, String startDate, String endDate) {
|
||||
StringBuilder sql = new StringBuilder("SELECT id, biz_type, account_id, direction, amount, pay_time FROM payments WHERE shop_id=?");
|
||||
java.util.List<Object> ps = new java.util.ArrayList<>();
|
||||
ps.add(shopId);
|
||||
if (direction != null && !direction.isBlank()) { sql.append(" AND direction=?"); ps.add(direction); }
|
||||
if (bizType != null && !bizType.isBlank()) { sql.append(" AND biz_type=?"); ps.add(bizType); }
|
||||
if (accountId != null) { sql.append(" AND account_id=?"); ps.add(accountId); }
|
||||
if (kw != null && !kw.isBlank()) { sql.append(" AND (CAST(id AS CHAR) LIKE ?)"); ps.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sql.append(" AND pay_time>=?"); ps.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sql.append(" AND pay_time<=?"); ps.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
sql.append(" ORDER BY pay_time DESC LIMIT ? OFFSET ?");
|
||||
ps.add(size); ps.add(page * size);
|
||||
java.util.List<java.util.Map<String,Object>> list = jdbcTemplate.queryForList(sql.toString(), ps.toArray());
|
||||
StringBuilder sumSql = new StringBuilder("SELECT COALESCE(SUM(amount),0) FROM payments WHERE shop_id=?");
|
||||
java.util.List<Object> sumPs = new java.util.ArrayList<>(); sumPs.add(shopId);
|
||||
if (direction != null && !direction.isBlank()) { sumSql.append(" AND direction=?"); sumPs.add(direction); }
|
||||
if (bizType != null && !bizType.isBlank()) { sumSql.append(" AND biz_type=?"); sumPs.add(bizType); }
|
||||
if (accountId != null) { sumSql.append(" AND account_id=?"); sumPs.add(accountId); }
|
||||
if (kw != null && !kw.isBlank()) { sumSql.append(" AND (CAST(id AS CHAR) LIKE ?)"); sumPs.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sumSql.append(" AND pay_time>=?"); sumPs.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sumSql.append(" AND pay_time<=?"); sumPs.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
java.math.BigDecimal total = jdbcTemplate.queryForObject(sumSql.toString(), java.math.BigDecimal.class, sumPs.toArray());
|
||||
java.util.Map<String,Object> resp = new java.util.HashMap<>(); resp.put("list", list); resp.put("totalAmount", total == null ? java.math.BigDecimal.ZERO : total); return resp;
|
||||
}
|
||||
|
||||
public java.util.Map<String,Object> listOtherTransactions(Long shopId, String type, Long accountId, String kw, int page, int size, String startDate, String endDate) {
|
||||
StringBuilder sql = new StringBuilder("SELECT id, `type`, account_id, amount, tx_time, remark FROM other_transactions WHERE shop_id=?");
|
||||
java.util.List<Object> ps = new java.util.ArrayList<>(); ps.add(shopId);
|
||||
if (type != null && !type.isBlank()) { sql.append(" AND `type`=?"); ps.add(type); }
|
||||
if (accountId != null) { sql.append(" AND account_id=?"); ps.add(accountId); }
|
||||
if (kw != null && !kw.isBlank()) { sql.append(" AND (remark LIKE ? OR category LIKE ?)"); ps.add('%'+kw+'%'); ps.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sql.append(" AND tx_time>=?"); ps.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sql.append(" AND tx_time<=?"); ps.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
sql.append(" ORDER BY tx_time DESC LIMIT ? OFFSET ?"); ps.add(size); ps.add(page * size);
|
||||
java.util.List<java.util.Map<String,Object>> list = jdbcTemplate.queryForList(sql.toString(), ps.toArray());
|
||||
StringBuilder sumSql = new StringBuilder("SELECT COALESCE(SUM(amount),0) FROM other_transactions WHERE shop_id=?");
|
||||
java.util.List<Object> sumPs = new java.util.ArrayList<>(); sumPs.add(shopId);
|
||||
if (type != null && !type.isBlank()) { sumSql.append(" AND `type`=?"); sumPs.add(type); }
|
||||
if (accountId != null) { sumSql.append(" AND account_id=?"); sumPs.add(accountId); }
|
||||
if (kw != null && !kw.isBlank()) { sumSql.append(" AND (remark LIKE ? OR category LIKE ?)"); sumPs.add('%'+kw+'%'); sumPs.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sumSql.append(" AND tx_time>=?"); sumPs.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sumSql.append(" AND tx_time<=?"); sumPs.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
java.math.BigDecimal total = jdbcTemplate.queryForObject(sumSql.toString(), java.math.BigDecimal.class, sumPs.toArray());
|
||||
java.util.Map<String,Object> resp = new java.util.HashMap<>(); resp.put("list", list); resp.put("totalAmount", total == null ? java.math.BigDecimal.ZERO : total); return resp;
|
||||
}
|
||||
|
||||
public java.util.Map<String,Object> listInventoryLogs(Long shopId, Long productId, String reason, String kw, int page, int size, String startDate, String endDate) {
|
||||
StringBuilder sql = new StringBuilder("SELECT id, product_id, qty_delta, amount_delta, reason, tx_time FROM inventory_movements WHERE shop_id=?");
|
||||
java.util.List<Object> ps = new java.util.ArrayList<>(); ps.add(shopId);
|
||||
if (productId != null) { sql.append(" AND product_id=?"); ps.add(productId); }
|
||||
if (reason != null && !reason.isBlank()) { sql.append(" AND reason=?"); ps.add(reason); }
|
||||
if (kw != null && !kw.isBlank()) { sql.append(" AND (remark LIKE ? OR source_type LIKE ?)"); ps.add('%'+kw+'%'); ps.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sql.append(" AND tx_time>=?"); ps.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sql.append(" AND tx_time<=?"); ps.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
sql.append(" ORDER BY tx_time DESC LIMIT ? OFFSET ?"); ps.add(size); ps.add(page * size);
|
||||
java.util.List<java.util.Map<String,Object>> list = jdbcTemplate.queryForList(sql.toString(), ps.toArray());
|
||||
StringBuilder sumSql = new StringBuilder("SELECT COALESCE(SUM(COALESCE(amount_delta,0)),0) FROM inventory_movements WHERE shop_id=?");
|
||||
java.util.List<Object> sumPs = new java.util.ArrayList<>(); sumPs.add(shopId);
|
||||
if (productId != null) { sumSql.append(" AND product_id=?"); sumPs.add(productId); }
|
||||
if (reason != null && !reason.isBlank()) { sumSql.append(" AND reason=?"); sumPs.add(reason); }
|
||||
if (kw != null && !kw.isBlank()) { sumSql.append(" AND (remark LIKE ? OR source_type LIKE ?)"); sumPs.add('%'+kw+'%'); sumPs.add('%'+kw+'%'); }
|
||||
if (startDate != null && !startDate.isBlank()) { sumSql.append(" AND tx_time>=?"); sumPs.add(java.sql.Timestamp.valueOf(startDate + " 00:00:00")); }
|
||||
if (endDate != null && !endDate.isBlank()) { sumSql.append(" AND tx_time<=?"); sumPs.add(java.sql.Timestamp.valueOf(endDate + " 23:59:59")); }
|
||||
java.math.BigDecimal total = jdbcTemplate.queryForObject(sumSql.toString(), java.math.BigDecimal.class, sumPs.toArray());
|
||||
java.util.Map<String,Object> resp = new java.util.HashMap<>(); resp.put("list", list); resp.put("totalAmount", total == null ? java.math.BigDecimal.ZERO : total); return resp;
|
||||
}
|
||||
|
||||
private static BigDecimal n(BigDecimal v) { return v == null ? BigDecimal.ZERO : v; }
|
||||
private static BigDecimal scale2(BigDecimal v) { return v.setScale(2, java.math.RoundingMode.HALF_UP); }
|
||||
private static LocalDateTime nowUtc() { return LocalDateTime.now(java.time.Clock.systemUTC()); }
|
||||
|
||||
private void ensureDefaultAccounts(Long shopId, Long userId) {
|
||||
// 为 cash/bank/wechat 分别确保存在一条账户记录;按 type→name 顺序检查,避免同名唯一冲突
|
||||
ensureAccount(shopId, userId, "cash", "现金");
|
||||
ensureAccount(shopId, userId, "bank", "银行存款");
|
||||
ensureAccount(shopId, userId, "wechat", "微信");
|
||||
}
|
||||
|
||||
private void ensureAccount(Long shopId, Long userId, String type, String name) {
|
||||
List<Long> byType = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND type=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, type);
|
||||
if (!byType.isEmpty()) return;
|
||||
List<Long> byName = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND name=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, name);
|
||||
if (!byName.isEmpty()) return; // 已有同名则直接复用,无需再插
|
||||
jdbcTemplate.update("INSERT INTO accounts (shop_id,user_id,name,type,balance,status,created_at,updated_at) VALUES (?,?,?,?,0,1,NOW(),NOW())",
|
||||
shopId, userId, name, type);
|
||||
}
|
||||
|
||||
private Long resolveAccountId(Long shopId, Long userId, String method) {
|
||||
String type = "cash";
|
||||
if ("bank".equalsIgnoreCase(method)) type = "bank";
|
||||
if ("wechat".equalsIgnoreCase(method)) type = "wechat";
|
||||
String name = "现金";
|
||||
if ("bank".equals(type)) name = "银行存款"; else if ("wechat".equals(type)) name = "微信";
|
||||
// 先按 type 查
|
||||
List<Long> byType = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND type=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, type);
|
||||
if (!byType.isEmpty()) return byType.get(0);
|
||||
// 再按 name 查,避免同名唯一冲突
|
||||
List<Long> byName = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND name=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, name);
|
||||
if (!byName.isEmpty()) return byName.get(0);
|
||||
// 都没有再插入
|
||||
jdbcTemplate.update("INSERT INTO accounts (shop_id,user_id,name,type,balance,status,created_at,updated_at) VALUES (?,?,?,?,0,1,NOW(),NOW())",
|
||||
shopId, userId, name, type);
|
||||
// 插入后按 type 读取
|
||||
List<Long> recheck = jdbcTemplate.query("SELECT id FROM accounts WHERE shop_id=? AND type=? LIMIT 1", (rs,rn)->rs.getLong(1), shopId, type);
|
||||
if (!recheck.isEmpty()) return recheck.get(0);
|
||||
throw new IllegalStateException("账户映射失败: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.example.demo.order.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class OrderDtos {
|
||||
|
||||
public static class CreateOrderRequest {
|
||||
public String type; // sale.out / sale.return / sale.collect / purchase.in / purchase.return / purchase.pay
|
||||
public String orderTime; // ISO8601 或 yyyy-MM-dd
|
||||
public Long customerId; // 可空
|
||||
public Long supplierId; // 可空
|
||||
public List<Item> items; // 出入库时必填
|
||||
public List<PaymentItem> payments; // 收款/付款时必填
|
||||
public BigDecimal amount; // 前端提供,后端将重算覆盖
|
||||
public String remark;
|
||||
}
|
||||
|
||||
public static class Item {
|
||||
public Long productId;
|
||||
public BigDecimal quantity;
|
||||
public BigDecimal unitPrice;
|
||||
public BigDecimal discountRate; // 可空,缺省 0
|
||||
}
|
||||
|
||||
public static class PaymentItem {
|
||||
public String method; // cash/bank/wechat
|
||||
public BigDecimal amount;
|
||||
public Long orderId; // 可选:若挂单则带上
|
||||
}
|
||||
|
||||
public static class CreateOrderResponse {
|
||||
public Long id;
|
||||
public String orderNo;
|
||||
public CreateOrderResponse(Long id, String orderNo) { this.id = id; this.orderNo = orderNo; }
|
||||
}
|
||||
|
||||
public static class CreatePaymentsResponse {
|
||||
public java.util.List<Long> paymentIds;
|
||||
public CreatePaymentsResponse(java.util.List<Long> ids) { this.paymentIds = ids; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ public class ProductDtos {
|
||||
public BigDecimal stock;
|
||||
public BigDecimal purchasePrice;
|
||||
public BigDecimal retailPrice;
|
||||
public BigDecimal distributionPrice;
|
||||
public BigDecimal wholesalePrice;
|
||||
public BigDecimal bigClientPrice;
|
||||
public List<Image> images;
|
||||
@@ -61,7 +60,6 @@ public class ProductDtos {
|
||||
public static class Prices {
|
||||
public BigDecimal purchasePrice;
|
||||
public BigDecimal retailPrice;
|
||||
public BigDecimal distributionPrice;
|
||||
public BigDecimal wholesalePrice;
|
||||
public BigDecimal bigClientPrice;
|
||||
}
|
||||
@@ -70,3 +68,6 @@ public class ProductDtos {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,3 +39,6 @@ public class Inventory {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -98,3 +98,6 @@ public class Product {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -56,3 +56,6 @@ public class ProductCategory {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -41,3 +41,6 @@ public class ProductImage {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -59,3 +59,6 @@ public class ProductPrice {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -46,3 +46,6 @@ public class ProductUnit {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,3 +15,6 @@ public interface CategoryRepository extends JpaRepository<ProductCategory, Long>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,3 +9,6 @@ public interface InventoryRepository extends JpaRepository<Inventory, Long> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,3 +13,6 @@ public interface ProductImageRepository extends JpaRepository<ProductImage, Long
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,3 +9,6 @@ public interface ProductPriceRepository extends JpaRepository<ProductPrice, Long
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,3 +23,6 @@ public interface ProductRepository extends JpaRepository<Product, Long> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,3 +14,6 @@ public interface UnitRepository extends JpaRepository<com.example.demo.product.e
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@ import com.example.demo.product.repo.ProductImageRepository;
|
||||
import com.example.demo.product.repo.ProductPriceRepository;
|
||||
import com.example.demo.product.repo.ProductRepository;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -27,35 +29,66 @@ public class ProductService {
|
||||
private final ProductPriceRepository priceRepository;
|
||||
private final InventoryRepository inventoryRepository;
|
||||
private final ProductImageRepository imageRepository;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public ProductService(ProductRepository productRepository,
|
||||
ProductPriceRepository priceRepository,
|
||||
InventoryRepository inventoryRepository,
|
||||
ProductImageRepository imageRepository) {
|
||||
ProductImageRepository imageRepository,
|
||||
JdbcTemplate jdbcTemplate) {
|
||||
this.productRepository = productRepository;
|
||||
this.priceRepository = priceRepository;
|
||||
this.inventoryRepository = inventoryRepository;
|
||||
this.imageRepository = imageRepository;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
public Page<ProductDtos.ProductListItem> search(Long shopId, String kw, Long categoryId, int page, int size) {
|
||||
Page<Product> p = productRepository.search(shopId, kw, categoryId, PageRequest.of(page, size));
|
||||
return p.map(prod -> {
|
||||
ProductDtos.ProductListItem it = new ProductDtos.ProductListItem();
|
||||
it.id = prod.getId();
|
||||
it.name = prod.getName();
|
||||
it.brand = prod.getBrand();
|
||||
it.model = prod.getModel();
|
||||
it.spec = prod.getSpec();
|
||||
// stock
|
||||
inventoryRepository.findById(prod.getId()).ifPresent(inv -> it.stock = inv.getQuantity());
|
||||
// price
|
||||
priceRepository.findById(prod.getId()).ifPresent(pr -> it.retailPrice = pr.getRetailPrice());
|
||||
// cover
|
||||
List<ProductImage> imgs = imageRepository.findByProductIdOrderBySortOrderAscIdAsc(prod.getId());
|
||||
it.cover = imgs.isEmpty() ? null : imgs.get(0).getUrl();
|
||||
return it;
|
||||
});
|
||||
try {
|
||||
Page<Product> p = productRepository.search(shopId, kw, categoryId, PageRequest.of(page, size));
|
||||
return p.map(prod -> {
|
||||
ProductDtos.ProductListItem it = new ProductDtos.ProductListItem();
|
||||
it.id = prod.getId();
|
||||
it.name = prod.getName();
|
||||
it.brand = prod.getBrand();
|
||||
it.model = prod.getModel();
|
||||
it.spec = prod.getSpec();
|
||||
inventoryRepository.findById(prod.getId()).ifPresent(inv -> it.stock = inv.getQuantity());
|
||||
priceRepository.findById(prod.getId()).ifPresent(pr -> it.retailPrice = pr.getRetailPrice());
|
||||
List<ProductImage> imgs = imageRepository.findByProductIdOrderBySortOrderAscIdAsc(prod.getId());
|
||||
it.cover = imgs.isEmpty() ? null : imgs.get(0).getUrl();
|
||||
return it;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
// 安全回退为 JDBC 查询,保障功能可用
|
||||
StringBuilder sql = new StringBuilder("SELECT p.id,p.name,p.brand,p.model,p.spec,\n" +
|
||||
"(SELECT i.quantity FROM inventories i WHERE i.product_id=p.id) AS stock,\n" +
|
||||
"(SELECT pr.retail_price FROM product_prices pr WHERE pr.product_id=p.id) AS retail_price,\n" +
|
||||
"(SELECT img.url FROM product_images img WHERE img.product_id=p.id ORDER BY img.sort_order, img.id LIMIT 1) AS cover\n" +
|
||||
"FROM products p WHERE p.shop_id=? AND p.deleted_at IS NULL");
|
||||
List<Object> ps = new ArrayList<>();
|
||||
ps.add(shopId);
|
||||
if (kw != null && !kw.isBlank()) { sql.append(" AND (p.name LIKE ? OR p.brand LIKE ? OR p.model LIKE ? OR p.spec LIKE ? OR p.barcode LIKE ?)");
|
||||
String like = "%" + kw + "%"; ps.add(like); ps.add(like); ps.add(like); ps.add(like); ps.add(like); }
|
||||
if (categoryId != null) { sql.append(" AND p.category_id=?"); ps.add(categoryId); }
|
||||
sql.append(" ORDER BY p.id DESC LIMIT ? OFFSET ?");
|
||||
ps.add(size); ps.add(page * size);
|
||||
List<ProductDtos.ProductListItem> list = jdbcTemplate.query(sql.toString(), (rs,rn) -> {
|
||||
ProductDtos.ProductListItem it = new ProductDtos.ProductListItem();
|
||||
it.id = rs.getLong("id");
|
||||
it.name = rs.getString("name");
|
||||
it.brand = rs.getString("brand");
|
||||
it.model = rs.getString("model");
|
||||
it.spec = rs.getString("spec");
|
||||
java.math.BigDecimal st = (java.math.BigDecimal) rs.getObject("stock");
|
||||
it.stock = st;
|
||||
java.math.BigDecimal rp = (java.math.BigDecimal) rs.getObject("retail_price");
|
||||
it.retailPrice = rp;
|
||||
it.cover = rs.getString("cover");
|
||||
return it;
|
||||
}, ps.toArray());
|
||||
return new PageImpl<>(list, PageRequest.of(page, size), list.size());
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<ProductDtos.ProductDetail> findDetail(Long id) {
|
||||
@@ -78,7 +111,6 @@ public class ProductService {
|
||||
priceRepository.findById(p.getId()).ifPresent(pr -> {
|
||||
d.purchasePrice = pr.getPurchasePrice();
|
||||
d.retailPrice = pr.getRetailPrice();
|
||||
d.distributionPrice = pr.getDistributionPrice();
|
||||
d.wholesalePrice = pr.getWholesalePrice();
|
||||
d.bigClientPrice = pr.getBigClientPrice();
|
||||
});
|
||||
@@ -171,12 +203,6 @@ public class ProductService {
|
||||
pr.setUserId(userId);
|
||||
pr.setPurchasePrice(nvl(prices.purchasePrice, BigDecimal.ZERO));
|
||||
pr.setRetailPrice(nvl(prices.retailPrice, BigDecimal.ZERO));
|
||||
// 前端不再传分销价:仅当入参提供时更新;新建记录若未提供则置 0
|
||||
if (prices.distributionPrice != null) {
|
||||
pr.setDistributionPrice(prices.distributionPrice);
|
||||
} else if (existed.isEmpty()) {
|
||||
pr.setDistributionPrice(BigDecimal.ZERO);
|
||||
}
|
||||
pr.setWholesalePrice(nvl(prices.wholesalePrice, BigDecimal.ZERO));
|
||||
pr.setBigClientPrice(nvl(prices.bigClientPrice, BigDecimal.ZERO));
|
||||
pr.setUpdatedAt(now);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
spring.application.name=demo
|
||||
|
||||
# 数据源配置(通过环境变量注入,避免硬编码)
|
||||
spring.datasource.url=${SPRING_DATASOURCE_URL}
|
||||
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
|
||||
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
|
||||
# 正确的配置
|
||||
# 格式为: jdbc:mysql://<主机名>:<端口号>/<数据库名>?参数
|
||||
# 默认附带 MySQL 8 推荐参数,避免握手/时区/编码问题
|
||||
spring.datasource.url=${DB_URL:jdbc:mysql://mysql.tonaspace.com:3306/partsinquiry?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8}
|
||||
|
||||
# 用户名和密码直接写值
|
||||
spring.datasource.username=${DB_USER:root}
|
||||
spring.datasource.password=${DB_PASSWORD:TONA1234}
|
||||
|
||||
# JPA 基本配置
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
|
||||
Reference in New Issue
Block a user