feat(shop): 新增商城基础设置组件

- 新增商城基础信息配置界面支持店铺名称、Logo、描述、电话、地址和开关配置
- 实现图片选择和删除功能,支持Logo的上传回显
- 集成表单校验和保存接口调用,提供保存状态反馈
- 优化响应式布局适配不同屏幕尺寸

fix(cms): 防止文章编辑内容的XSS攻击

- 在文章编辑组件中对动态HTML内容添加DOMPurify消毒
- 替换 v-html 渲染为安全消毒后的内容展现
- 确保富文本内容安全,防止跨站脚本漏洞

refactor(system-setting): 优化系统设置基本信息组件逻辑

- 替换ico文件上传组件,改用SelectFile实现图片选择和删除功能
- 简化图标上传流程,移除上传接口调用相关代码
- 统一表单数据处理,增强设置数据解析和回显兼容性
- 调整保存逻辑,支持根据是否存在主键调用新增或更新接口
- 改进watch数据响应逻辑,支持多种数据结构兼容

fix(system-setting): 修正清理设置组件数据重置逻辑

- 统一清理设置组件的 settingKey 值为 clear,避免混淆
- 优化数据监听回调,支持不同数据结构和空数据重置表单
- 确保组件初始化状态正确,避免遗留数据影响展示

fix(store): 修正 chat store 定义方式

- 按 pinia 官方规范简化 store 定义参数
- 修复 store id 错误传递问题,确保正确注册和使用
This commit is contained in:
2026-06-16 12:52:30 +08:00
parent c82ae7ee06
commit de93292fa2
313 changed files with 44898 additions and 25567 deletions

View File

@@ -0,0 +1,232 @@
# 优惠券模块后端改造方案
> 前端 Vue 改造已完成paopao-vue本文档为后端 Java 端对应的改动清单。
---
## 一、DDL — shop_coupon 表新增字段
```sql
-- ============================================================
-- 优惠券模块升级:新增发放对象控制 + 场地使用券支持
-- 执行前请先备份表数据!
-- ============================================================
ALTER TABLE `shop_coupon`
-- 发放对象(0全部用户 1仅会员 2仅非会员 3指定用户)
ADD COLUMN `receive_target` TINYINT NOT NULL DEFAULT 0 COMMENT '发放对象(0全部用户 1仅会员 2仅非会员 3指定用户)' AFTER `enabled`,
-- 指定用户ID列表(JSON数组)receive_target=3 时使用
ADD COLUMN `receive_user_ids` VARCHAR(1000) DEFAULT NULL COMMENT '指定用户ID列表(JSON数组格式)receiveTarget=3时使用' AFTER `receive_target`,
-- 场地使用券相关字段 (type=50 时使用)
ADD COLUMN `venue_type` TINYINT DEFAULT NULL COMMENT '场地使用券-场地类型' AFTER `receive_user_ids`,
ADD COLUMN `venue_id` INT DEFAULT NULL COMMENT '场地使用券-指定场地ID' AFTER `venue_type`,
ADD COLUMN `use_count` INT NOT NULL DEFAULT -1 COMMENT '场地使用券-可用次数(-1无限制)' AFTER `venue_id`,
ADD COLUMN `use_duration` INT DEFAULT NULL COMMENT '场地使用券-使用时长(分钟)' AFTER `use_count`;
```
### 字段说明
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `receive_target` | TINYINT | `0` | 0=全部, 1=仅会员, 2=仅非会员, 3=指定用户 |
| `receive_user_ids` | VARCHAR(1000) | NULL | JSON 数组如 `[101,202,303]`, receiveTarget=3 时有值 |
| `venue_type` | TINYINT | NULL | 场地类型type=50 时填写 |
| `venue_id` | INT | NULL | 具体场地 IDtype=50 时填写 |
| `use_count` | INT | `-1` | 可用次数,-1=不限制 |
| `use_duration` | INT | NULL | 使用时长(分钟) |
---
## 二、Java Entity — ShopCoupon.java 新增字段
在实体类中新增以下字段(与前端 model/index.ts 对齐):
```java
// ========== 以下是新增字段 ==========
/**
* 发放对象(0全部用户 1仅会员 2仅非会员 3指定用户)
*/
@TableField("receive_target")
private Integer receiveTarget;
/**
* 指定用户ID列表(JSON数组格式)receiveTarget=3时使用
* 存储格式: ["101","202","303"] 或 [101,202,303]
*/
@TableField("receive_user_ids")
private String receiveUserIds;
/**
* 场地使用券-场地类型 (type=50时使用)
*/
@TableField("venue_type")
private Integer venueType;
/**
* 场地使用券-指定场地ID (type=50时使用)
*/
@TableField("venue_id")
private Integer venueId;
/**
* 场地使用券-可用次数(-1表示无限制)
*/
@TableField("use_count")
private Integer useCount;
/**
* 场地使用券-使用时长(分钟)
*/
@TableField("use_duration")
private Integer useDuration;
```
> 注意:如果项目使用了 Lombok `@Data` 或手动 getter/setter确保新字段也有对应的访问方法。
---
## 三、领券/用券校验逻辑
### 3.1 领券接口校验(用户领取优惠券时)
在领券 Controller 或 Service 中增加校验:
```java
/**
* 校验用户是否有资格领取该优惠券
*
* @param coupon 优惠券信息
* @param userId 当前用户ID
* @param userGradeId 用户会员等级ID (0=非会员, >0=会员)
* @throws BusinessException 校验不通过时抛出业务异常
*/
public void validateCouponReceiveTarget(ShopCoupon coupon, Long userId, Integer userGradeId) {
Integer target = coupon.getReceiveTarget();
// target=0 全部用户可领,直接通过
if (target == null || target == 0) {
return;
}
// target=1 仅会员可领
if (target == 1) {
if (userGradeId == null || userGradeId <= 0) {
throw new BusinessException("该优惠券仅限会员领取");
}
return;
}
// target=2 仅非会员可领
if (target == 2) {
if (userGradeId != null && userGradeId > 0) {
throw new BusinessException("该优惠券仅限非会员领取");
}
return;
}
// target=3 指定用户可领
if (target == 3) {
String receiveUserIds = coupon.getReceiveUserIds();
if (StringUtils.isBlank(receiveUserIds)) {
throw new BusinessException("该优惠券未设置指定用户");
}
// 解析 JSON 数组
List<Long> allowedUserIds = JsonUtils.parseArray(receiveUserIds, Long.class);
if (allowedUserIds == null || !allowedUserIds.contains(userId)) {
throw new BusinessException("您不在该优惠券的发放范围内");
}
return;
}
// 未知的 target 值,默认放行(向后兼容)
}
```
### 3.2 调用位置建议
```
用户点击"领取优惠券"
┌──────────────────────┐
│ 1. 检查优惠券是否启用 │
│ 2. 检查是否已过期 │
│ 3. 检查发放数量是否已完 │
│ 4. 检查每人限领数量 │ ◄── 在此步骤之后、实际发券之前插入
│ ★5. 校验发放对象 ★ │ validateCouponReceiveTarget()
│ 6. 创建用户优惠券记录 │
└──────────────────────┘
```
### 3.3 用券/下单时的校验(可选增强)
下单抵扣时除了常规的金额/时间/商品范围校验外,可追加:
```java
/**
* 下单使用优惠券时的额外校验
*/
public void validateCouponForOrder(ShopCoupon coupon, ShopUser user, OrderContext ctx) {
// ... 已有的金额/有效期/适用范围校验 ...
// 场地使用券(type=50)特殊校验
if (coupon.getType() != null && coupon.getType() == 50) {
// 验证订单是否包含场地服务
if (!ctx.hasVenueItem()) {
throw new BusinessException("该券仅可用于场地预订");
}
// 如果指定了场地类型/ID校验是否匹配
if (coupon.getVenueType() != null && !coupon.getVenueType().equals(ctx.getVenueType())) {
throw new BusinessException("场地类型不匹配");
}
if (coupon.getVenueId() != null && !coupon.getVenueId().equals(ctx.getVenueId())) {
throw new BusinessException("指定场地不匹配");
}
}
}
```
---
## 四、JSON 工具方法说明
`receive_user_ids` 字段的读写需要 JSON 序列化/反序列化:
**写入(保存优惠券时):**
```java
// 前端传来的是 JSON 字符串 "[101,202,303]",直接存即可
coupon.setReceiveUserIds(receiveUserIdsJsonString);
```
**读取(校验时解析):**
```java
// 推荐使用 Jackson / Fastjson / Gson (项目已有的 JSON 库)
// 示例Jackson
ObjectMapper mapper = new ObjectMapper();
List<Long> userIds = mapper.readValue(coupon.getReceiveUserIds(),
new TypeReference<List<Long>>() {});
```
---
## 五、前端-后端字段对照表
| 前端字段 (TypeScript) | 后端字段 (Java/DB) | 类型 |
|------------------------|--------------------|------|
| `receiveTarget` | `receive_target` | Integer/TINYINT |
| `receiveUserIds` | `receive_user_ids` | String/VARCHAR |
| `venueType` | `venue_type` | Integer/TINYINT |
| `venueId` | `venue_id` | Integer/INT |
| `useCount` | `use_count` | Integer/INT |
| `useDuration` | `use_duration` | Integer/INT |
---
## 六、注意事项
1. **向后兼容**`receive_target` 默认值 `0` 表示全部用户,不影响已有优惠券的领用行为
2. **JSON 格式一致性**:前端提交时已序列化为 JSON 字符串,后端直接存储;读取时按 JSON 解析
3. **场地使用券 (type=50)** 是独立于普通商品券的类型,下单系统需单独适配其核销逻辑
4. **`gradeId` 判断会员**:依赖 `shop_user.grade_id` 字段,`grade_id > 0` 为会员,`=0``NULL` 为非会员
5. **建议先在测试环境执行 DDL 并验证完整流程后再上生产**