This commit is contained in:
2025-09-16 22:11:19 +08:00
parent 562ec4abf9
commit 46c5682960
65 changed files with 1997 additions and 56 deletions

View File

@@ -13,6 +13,20 @@
- User: `root`
- 说明:所有结构变更均通过 MysqlMCP 执行并已落地到线上库。
### 角色与模拟数据策略(统一为店长)
- 当前不进行角色划分,系统仅保留“店长”角色。
- 已将所有用户记录统一为:`role='owner'``is_owner=1`
- 前端/后端权限逻辑暂未启用,后续若引入权限体系,再行扩展角色与边界。
### 小程序默认用户(可开关,默认关闭)
- 目的:开发/演示阶段,便于免登录联调。
- 机制:前端在请求头附加 `X-User-Id`(值为张老板 id=2仅当开关开启时。
- 开关:
- 环境变量:`VITE_APP_ENABLE_DEFAULT_USER=true``VITE_APP_DEFAULT_USER_ID=2`
- 或本地存储:`ENABLE_DEFAULT_USER=true``DEFAULT_USER_ID=2`
- 关闭:不设置/置为 `false` 即可停用(生产环境默认关闭)。
- 完全移除:删除 `frontend/common/config.js` 中默认用户配置与 `frontend/common/http.js` 中注入逻辑。
### 后端Spring Boot数据库状态
- 依赖:`pom.xml` 未包含 `spring-boot-starter-web``spring-boot-starter-data-jpa``mysql-connector-j` 等数据库相关依赖。
- 配置:`src/main/resources/application.properties` 仅有 `spring.application.name=demo`;未配置 `spring.datasource.*``spring.jpa.*`

View File

@@ -0,0 +1,27 @@
package com.example.demo.dashboard;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/dashboard")
public class DashboardController {
private final DashboardService dashboardService;
public DashboardController(DashboardService dashboardService) {
this.dashboardService = dashboardService;
}
@GetMapping("/overview")
public ResponseEntity<DashboardOverviewResponse> overview(@RequestHeader(name = "X-Shop-Id", required = false) Long shopId) {
long sid = (shopId == null ? 1L : shopId);
return ResponseEntity.ok(dashboardService.getOverview(sid));
}
}

View File

@@ -0,0 +1,30 @@
package com.example.demo.dashboard;
import java.math.BigDecimal;
public class DashboardOverviewResponse {
private BigDecimal todaySalesAmount;
private BigDecimal monthGrossProfit;
private BigDecimal stockTotalQuantity;
public DashboardOverviewResponse(BigDecimal todaySalesAmount, BigDecimal monthGrossProfit, BigDecimal stockTotalQuantity) {
this.todaySalesAmount = todaySalesAmount;
this.monthGrossProfit = monthGrossProfit;
this.stockTotalQuantity = stockTotalQuantity;
}
public BigDecimal getTodaySalesAmount() {
return todaySalesAmount;
}
public BigDecimal getMonthGrossProfit() {
return monthGrossProfit;
}
public BigDecimal getStockTotalQuantity() {
return stockTotalQuantity;
}
}

View File

@@ -0,0 +1,56 @@
package com.example.demo.dashboard;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
@Repository
public class DashboardRepository {
@PersistenceContext
private EntityManager entityManager;
public BigDecimal sumTodaySalesOrders(Long shopId) {
Object result = entityManager.createNativeQuery(
"SELECT COALESCE(SUM(amount), 0) FROM sales_orders " +
"WHERE shop_id = :shopId AND status = 'approved' AND DATE(order_time) = CURRENT_DATE()"
).setParameter("shopId", shopId).getSingleResult();
return toBigDecimal(result);
}
public BigDecimal sumMonthGrossProfitApprox(Long shopId) {
Object result = entityManager.createNativeQuery(
"SELECT COALESCE(SUM(soi.amount - soi.quantity * COALESCE(pp.purchase_price, 0)), 0) AS gp " +
"FROM sales_orders so " +
"JOIN sales_order_items soi ON soi.order_id = so.id " +
"LEFT JOIN product_prices pp ON pp.product_id = soi.product_id AND pp.shop_id = so.shop_id " +
"WHERE so.shop_id = :shopId AND so.status = 'approved' " +
"AND so.order_time >= DATE_FORMAT(CURRENT_DATE(), '%Y-%m-01') " +
"AND so.order_time < DATE_ADD(DATE_FORMAT(CURRENT_DATE(), '%Y-%m-01'), INTERVAL 1 MONTH)"
).setParameter("shopId", shopId).getSingleResult();
return toBigDecimal(result);
}
public BigDecimal sumTotalInventoryQty(Long shopId) {
Object result = entityManager.createNativeQuery(
"SELECT COALESCE(SUM(quantity), 0) FROM inventories WHERE shop_id = :shopId"
).setParameter("shopId", shopId).getSingleResult();
return toBigDecimal(result);
}
private BigDecimal toBigDecimal(Object value) {
if (value == null) return BigDecimal.ZERO;
if (value instanceof BigDecimal) return (BigDecimal) value;
if (value instanceof Number) return BigDecimal.valueOf(((Number) value).doubleValue());
try {
return new BigDecimal(value.toString());
} catch (Exception e) {
return BigDecimal.ZERO;
}
}
}

View File

@@ -0,0 +1,24 @@
package com.example.demo.dashboard;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service
public class DashboardService {
private final DashboardRepository dashboardRepository;
public DashboardService(DashboardRepository dashboardRepository) {
this.dashboardRepository = dashboardRepository;
}
public DashboardOverviewResponse getOverview(long shopId) {
BigDecimal todaySales = dashboardRepository.sumTodaySalesOrders(shopId);
BigDecimal monthGrossProfit = dashboardRepository.sumMonthGrossProfitApprox(shopId);
BigDecimal stockTotalQty = dashboardRepository.sumTotalInventoryQty(shopId);
return new DashboardOverviewResponse(todaySales, monthGrossProfit, stockTotalQty);
}
}