系统架构与公共模块.md 12 KB

系统架构与公共模块说明

本文档描述项目整体架构、双应用部署、模块依赖、公共组件及开发规范要点。 帮助开发人员快速理解“为什么这样设计”以及各环节的特殊处理方式。


一、系统架构

1.1 双应用架构

应用 模块 端口 Context-Path 用途
AdminApplication haha-admin 7070 /admin 运营平台后端
MiniappApplication haha-miniapp 7077 /api 小程序后端

两个应用共享 haha-common / haha-entity / haha-mapper / haha-service,通过 @ComponentScan(basePackages = {"com.haha"})@MapperScan("com.haha.mapper") 自动扫描。

为什么采用双应用而非单应用?

运营平台(haha-admin)和小程序后端(haha-miniapp)的认证方式、API风格、安全要求完全不同:

  • 运营平台:Sa-Token 管理员登录,权限控制严格(@RequirePermission),仅运营人员访问
  • 小程序:微信登录 + accessToken,面向所有用户,无权限注解

分离部署的优势:

  • 独立扩缩容:小程序端流量远大于运营端
  • 安全隔离:运营平台不暴露给用户端
  • 独立发布:两端可独立上线,互不影响

1.2 模块依赖方向(严格单向)

haha-admin  -->  haha-service  -->  haha-mapper  -->  haha-entity
haha-miniapp -->  haha-service  -->  haha-mapper  -->  haha-entity
                                        |
haha-common <---------------------------+(所有模块均可依赖)
haha-sdk(独立模块,不依赖其他内部模块)

禁止反向依赖

  • haha-mapper 不可依赖 haha-service
  • haha-entity 不可依赖 haha-mapper 或 haha-service
  • haha-common 不可依赖任何其他内部模块
  • haha-admin 与 haha-miniapp 不可互相依赖

1.3 技术栈版本

技术 版本 说明
Java 21 禁止使用 Java 22+ 特性
Spring Boot 4.0.3 使用 Jakarta 命名空间
MyBatis-Plus 3.5.16 mybatis-plus-spring-boot4-starter
Sa-Token 1.45.0 sa-token-spring-boot3-starter
Hutool 5.8.40 禁止使用 hutool 6.x API
FastJson2 2.0.53 禁止使用 fastjson (v1)
Lombok 1.18.36 --
Jakarta EE Jakarta 非 Javax

二、前端应用

2.1 运营平台(haha-admin-web)

  • 框架:Vue 3 + Element Plus + TypeScript
  • 构建:Vite
  • 状态管理:Pinia
  • 路由:Vue Router

2.2 用户小程序(haha-mp)

  • 框架:UniApp + Vue 3 + TypeScript
  • 构建:Vite
  • 微信登录 + 支付分集成

2.3 管理端小程序(haha-admin-mp)

  • 框架:UniApp + Vue 3 + TypeScript
  • 用途:合作商管理、门店巡检等

三、公共模块 (haha-common)

3.1 枚举类 (com.haha.common.enums)

枚举 说明 code类型
OrderStatus 订单状态 int
PayStatus 支付状态 String
PaymentChannel 支付渠道 String
DeviceDoorStatus 门状态 String
DeviceAlertType 告警类型 String
ActivityTypeEnum 活动类型 int
ActivityStatusEnum 活动状态 int
CouponTypeEnum 优惠券类型 int
CouponStatusEnum 优惠券状态 int
DiscountTypeEnum 折扣类型 int
ValidTypeEnum 有效期类型 int
DistributeTypeEnum 发放类型 int
ApplyScopeEnum 适用范围 int
OpenType 开门类型 String
InviteStatusEnum 邀请状态 int
InviteRewardStatusEnum 邀请奖励状态 int
PayScoreState 支付分状态 String

枚举规范

  • 只含 code + description/label 字段
  • 必须提供 fromCode() / getByCode() 静态方法
  • 数据库存储 code 值,禁止存储 description

3.2 常量类 (com.haha.common.constant)

常量类 说明
OrderConstants 订单状态/支付状态/订单类型常量
DeviceConstants 设备相关常量
MarketingConstants 营销相关常量
RedisConstants Redis Key 定义
CallbackConstants 回调类型常量
CommonConstants 通用常量
ResponseEnum 业务响应码枚举

3.3 通用VO

VO 说明
Result<T> 统一返回封装(code/message/data)
PageResult<T> 分页返回(通过 PageResult.of(IPage<T>) 转换)
StatusLabel 状态标签(label + color)
OrderVO 订单详情VO

3.4 统一返回格式

{
  "code": 200,
  "message": "操作成功",
  "data": { ... }
}
场景 code message
成功 200 操作成功 / 具体描述
参数错误 400 字段错误描述
未登录 401 未登录 / token已过期
无权限 403 无访问权限
资源不存在 404 xxx不存在
业务异常 自定义 BusinessException 消息
系统异常 500 系统繁忙,请稍后重试

四、哈哈SDK (haha-sdk)

4.1 用途

独立模块,封装与哈哈平台(智能售卖机设备平台)的API交互,不依赖其他内部模块。

4.2 核心功能

功能 说明
设备API 开门、查询状态、设置温度/音量
商品API 商品查询、同步
订单API 创建订单、查询订单
回调验证 签名校验

4.3 认证方式

通过 authToken 进行设备级认证,SDK内部处理签名和请求构建。


五、权限体系

5.1 运营平台权限(Sa-Token)

  • 权限注解:@RequirePermission("module:operation")
  • operation 标准值:read / create / update / delete / other
  • 小程序端不需要 @RequirePermission(通过 accessToken 鉴权)

5.2 数据权限 (DataPermission)

支持按门店维度的数据隔离,运营人员只能查看授权门店的数据。

5.3 操作日志

  • 注解:@Log(module = "模块名", operation = OperationType.XXX, summary = "描述")
  • OperationType:CREATE / UPDATE / DELETE / OTHER
  • 读操作不加 @Log

六、数据库规范

6.1 表命名

  • 前缀 t_,如 t_ordert_device
  • 关联表:t_{主实体}_{关联实体},如 t_activity_devicet_coupon_shop

6.2 ID策略

  • 全局雪花算法:@TableId(type = IdType.ASSIGN_ID)
  • 数据库列类型:BIGINT
  • 禁止自增ID

6.3 字段命名

  • 数据库列:下划线(create_time, pay_status)
  • Java字段:驼峰(createTime, payStatus)
  • 自动映射:map-underscore-to-camel-case: true

6.4 XML映射文件

  • 位置:haha-mapper/src/main/resources/mapper/XxxMapper.xml
  • 必须定义 resultMap,禁止 resultType
  • 逻辑删除不会自动添加到手写SQL中

七、数据库表总览

核心业务表

表名 说明
t_order 订单表
t_order_goods 订单商品明细
t_order_item 订单商品项
t_device 设备表
t_shop 门店表
t_product 商品表
t_device_inventory 设备库存表
t_door_record 开关门记录表
t_user 用户表
t_account 用户账户表

营销优惠表

表名 说明
t_marketing_activity 营销活动表
t_activity_shop 活动-门店关联
t_activity_device 活动-设备关联
t_activity_product 活动-商品关联
t_coupon_template 优惠券模板表
t_user_coupon 用户优惠券表
t_coupon_shop 优惠券-门店关联
t_coupon_product 优惠券-商品关联
t_coupon_distribute 优惠券发放记录
t_timed_discount_activity 定时折扣活动表
t_timed_discount_device 定时折扣-设备关联
t_timed_discount_shop 定时折扣-门店关联
t_timed_discount_product 定时折扣-商品关联
t_timed_discount_record 定时折扣执行记录
t_timed_discount_statistics 定时折扣统计
t_invite_activity 邀请活动表
t_invite_record 邀请记录表
t_invite_reward 邀请奖励表
t_marketing_statistics 营销统计表

库存管理表

表名 说明
t_stock_record 补货记录表
t_stock_record_item 补货明细表
t_inventory_log 库存变更日志
t_price_adjustment_log 价格调整日志

系统管理表

表名 说明
t_admin 管理员表
t_role 角色表
t_role_permission 角色权限关联
t_data_permission 数据权限表
t_operation_log 操作日志表
t_dict_type 字典类型表
t_dict_data 字典数据表
t_device_alert_record 设备告警记录
t_announcement 公告表

分销模块表

表名 说明
t_distributor 合作商表
t_distributor_commission 佣金记录表
t_distributor_config 合作商配置表
t_distributor_referral 推荐记录表
t_distributor_withdrawal 提现记录表
t_distributor_monthly_report 月度报表

统计报表表

表名 说明
t_stat_category_daily 品类日统计
t_stat_device_daily 设备日统计
t_stat_product_daily 商品日统计
t_stat_shop_daily 门店日统计
t_stat_user_repurchase 用户复购统计

八、层模板管理

8.1 LayerTemplate 实体

设备货架层配置模板,定义每一层的商品排列方式。

8.2 核心功能

功能 说明
模板CRUD 创建/编辑/删除层模板
层配置 每层的商品类型、数量、位置
设备应用 将模板应用到指定设备

九、开发规范要点

  1. Long类型序列化:所有 Long 类型 ID 和外键字段必须添加 @JsonSerialize(using = ToStringSerializer.class) 防精度丢失
    • 原因:JavaScript 的 Number 类型最大安全整数为 2^53-1,超过则精度丢失。雪花算法生成的ID约19位数字,远超此限制
    • 影响:不添加此注解,前端接收到的ID末尾会被截断为0,导致根据ID查询/更新失败
  2. 日期格式@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
  3. 金额字段:必须使用 BigDecimal,禁止 double / float
    • 原因:浮点数在计算机中无法精确表示十进制小数,如 0.1 + 0.2 != 0.3
    • 运算:使用 setScale(2, RoundingMode.HALF_UP) 保留2位小数
  4. 依赖注入:构造器注入(final + @RequiredArgsConstructor),循环依赖时用 @Autowired @Lazy
  5. 事务:写操作必须 @Transactional(rollbackFor = Exception.class)
    • 注意rollbackFor = Exception.class 不能省略,默认只回滚 RuntimeException
  6. 分页校验page < 1 设为 1,pageSize > 100 设为 10
  7. 业务异常:使用 BusinessException + ResponseEnum,禁止 Controller 中抛出未处理异常
  8. 雪花算法ID注意:手写SQL中的逻辑删除条件不会自动添加,需手动加 WHERE deleted = 0

十、关键设计决策记录

10.1 为什么用雪花算法而非自增ID?

系统需要与哈哈平台(设备端)交互,设备端生成 activityId 关联开关门和订单。自增ID依赖数据库生成,在分布式场景和回调场景下不可用。雪花算法在应用层生成ID,不依赖数据库,且全局唯一。

迁移注意:项目早期使用自增ID,后改为雪花算法。已有数据需保留原ID,新数据使用雪花算法。alter_snowflake_id.sql 处理了迁移逻辑。

10.2 为什么 haha-sdk 是独立模块?

haha-sdk 封装与哈哈平台的HTTP交互,不依赖任何内部业务模块。这样设计是因为:

  • SDK可独立发布给第三方使用
  • 避免循环依赖(如果SDK依赖service,service又调用SDK)
  • SDK的更新不影响业务模块的编译

10.3 MyBatis-Plus 逻辑删除的特殊处理

MyBatis-Plus 的 @TableLogic 注解在 MP 自动生成的 SQL 中会自动添加 WHERE deleted = 0,但手写SQL(XML或注解式)不会自动添加。开发手写SQL时必须手动处理逻辑删除条件,否则会查询到已删除的数据。