diff --git a/par-core/src/main/java/com/par/core/entity/RiskRule.java b/par-core/src/main/java/com/par/core/entity/RiskRule.java index 4e5ec2e..e3a92fb 100644 --- a/par-core/src/main/java/com/par/core/entity/RiskRule.java +++ b/par-core/src/main/java/com/par/core/entity/RiskRule.java @@ -15,6 +15,9 @@ public class RiskRule { @TableId(type = IdType.AUTO) private Long id; + @TableField("site_id") + private String siteId; + @TableField("site_type") private String siteType; diff --git a/par-core/src/main/java/com/par/core/service/impl/RiskRuleServiceImpl.java b/par-core/src/main/java/com/par/core/service/impl/RiskRuleServiceImpl.java index ee1c9e7..d3acc62 100644 --- a/par-core/src/main/java/com/par/core/service/impl/RiskRuleServiceImpl.java +++ b/par-core/src/main/java/com/par/core/service/impl/RiskRuleServiceImpl.java @@ -22,11 +22,22 @@ public class RiskRuleServiceImpl implements RiskRuleService { private final com.par.core.storage.CachedStorageService storageService; private static final ObjectMapper om = new ObjectMapper(); + /** Find by siteId first, fallback to siteType for backward compat */ + private RiskRule findByIdentifier(String identifier) { + // Try siteId first + RiskRule rr = mapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(RiskRule::getSiteId, identifier).eq(RiskRule::getDeleted, 0) + .orderByDesc(RiskRule::getId).last("limit 1")); + if (rr != null) return rr; + // Fallback to siteType + return mapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(RiskRule::getSiteType, identifier).eq(RiskRule::getDeleted, 0) + .orderByDesc(RiskRule::getId).last("limit 1")); + } + @Override public Map getRules(String siteType) { - RiskRule rr = mapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .eq(RiskRule::getSiteType, siteType).eq(RiskRule::getDeleted, 0) - .orderByDesc(RiskRule::getId).last("limit 1")); + RiskRule rr = findByIdentifier(siteType); if (rr == null) return null; try { List> rules = om.readValue(rr.getRulesJson(), @@ -37,16 +48,14 @@ public class RiskRuleServiceImpl implements RiskRuleService { result.put("rules", rules); return result; } catch (Exception e) { - log.error("解析风险规则失败 siteType={}", siteType, e); + log.error("解析风险规则失败 key={}", siteType, e); return null; } } @Override public int getVersion(String siteType) { - RiskRule rr = mapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .eq(RiskRule::getSiteType, siteType).eq(RiskRule::getDeleted, 0) - .orderByDesc(RiskRule::getId).last("limit 1")); + RiskRule rr = findByIdentifier(siteType); return rr != null ? rr.getVersion() : 0; } @@ -55,19 +64,17 @@ public class RiskRuleServiceImpl implements RiskRuleService { try { String json = om.writerWithDefaultPrettyPrinter().writeValueAsString(rules); int nextVer = getVersion(siteType) + 1; - // 存 DB RiskRule rr = new RiskRule(); - rr.setSiteType(siteType); + rr.setSiteId(siteType); // siteType param now carries the identifier (siteId) rr.setVersion(nextVer); rr.setRulesJson(json); mapper.insert(rr); - // 存七牛云 + 本地缓存 String storagePath = String.format("rules/%s/v%d.json", siteType, nextVer); storageService.store(storagePath, json); versionService.incrementByType(siteType); - log.info("风险规则已保存: siteType={}, version={}, path={}", siteType, nextVer, storagePath); + log.info("风险规则已保存: key={}, version={}, path={}", siteType, nextVer, storagePath); } catch (Exception e) { - log.error("保存风险规则失败 siteType={}", siteType, e); + log.error("保存风险规则失败 key={}", siteType, e); throw new RuntimeException("保存失败: " + e.getMessage()); } } @@ -75,8 +82,13 @@ public class RiskRuleServiceImpl implements RiskRuleService { @Override public List> listVersions(String siteType) { var list = mapper.selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .eq(RiskRule::getSiteType, siteType).eq(RiskRule::getDeleted, 0) + .eq(RiskRule::getSiteId, siteType).eq(RiskRule::getDeleted, 0) .orderByDesc(RiskRule::getId)); + if (list.isEmpty()) { + list = mapper.selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(RiskRule::getSiteType, siteType).eq(RiskRule::getDeleted, 0) + .orderByDesc(RiskRule::getId)); + } List> result = new ArrayList<>(); for (var r : list) { result.add(Map.of("version", r.getVersion(), "createdAt", r.getCreatedAt() != null ? r.getCreatedAt().toString() : "")); @@ -87,13 +99,20 @@ public class RiskRuleServiceImpl implements RiskRuleService { @Override public String getVersionContent(String siteType, int version) { var r = mapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .eq(RiskRule::getSiteType, siteType).eq(RiskRule::getVersion, version) + .eq(RiskRule::getSiteId, siteType).eq(RiskRule::getVersion, version) .eq(RiskRule::getDeleted, 0)); + if (r == null) { + r = mapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(RiskRule::getSiteType, siteType).eq(RiskRule::getVersion, version) + .eq(RiskRule::getDeleted, 0)); + } return r != null ? r.getRulesJson() : null; } @Override public void deleteRules(String siteType) { + mapper.delete(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(RiskRule::getSiteId, siteType).eq(RiskRule::getDeleted, 0)); mapper.delete(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() .eq(RiskRule::getSiteType, siteType).eq(RiskRule::getDeleted, 0)); } @@ -108,6 +127,7 @@ public class RiskRuleServiceImpl implements RiskRuleService { Map m = new LinkedHashMap<>(); m.put("id", rr.getId()); m.put("siteType", rr.getSiteType()); + m.put("siteId", rr.getSiteId()); m.put("version", rr.getVersion()); m.put("updatedAt", rr.getUpdatedAt()); result.add(m); diff --git a/par-core/src/main/resources/db/migration/V15__risk_rules_site_id.sql b/par-core/src/main/resources/db/migration/V15__risk_rules_site_id.sql new file mode 100644 index 0000000..54abdeb --- /dev/null +++ b/par-core/src/main/resources/db/migration/V15__risk_rules_site_id.sql @@ -0,0 +1,2 @@ +ALTER TABLE risk_rules ADD COLUMN IF NOT EXISTS site_id VARCHAR(255) DEFAULT NULL AFTER site_type; +CREATE INDEX IF NOT EXISTS idx_risk_rules_site_id ON risk_rules(site_id); diff --git a/web/src/views/RiskRules.vue b/web/src/views/RiskRules.vue index 1d59f7e..2b28ea9 100644 --- a/web/src/views/RiskRules.vue +++ b/web/src/views/RiskRules.vue @@ -299,7 +299,7 @@ const parsed = computed({ const editSiteName = computed(() => { if (!editType.value) return '' - const site = (sites.value||[]).find(s => s.siteType === editType.value) + const site = (sites.value||[]).find(s => s.siteId === editType.value) return site ? (site.names||[]).join('/') || site.siteId || editType.value : editType.value }) @@ -334,8 +334,8 @@ const ruleText = (r) => { } const merged = computed(() => (Array.isArray(sites.value)?sites.value:[]).map(s => ({ siteId: s.siteId, names: s.names, siteType: s.siteType, - ruleCount: rulesMap.value[s.siteType]?.ruleCount || 0, version: rulesMap.value[s.siteType]?.version || null, - updatedAt: rulesMap.value[s.siteType]?.updatedAt || null, rulesJson: rulesMap.value[s.siteType]?.rulesJson || null, + ruleCount: rulesMap.value[s.siteId]?.ruleCount || 0, version: rulesMap.value[s.siteId]?.version || null, + updatedAt: rulesMap.value[s.siteId]?.updatedAt || null, rulesJson: rulesMap.value[s.siteId]?.rulesJson || null, }))) // ============ 数据操作 ============ @@ -348,17 +348,18 @@ const fetchAll = async () => { const map = {} for (const r of rulesData) { const cnt = (() => { try { return JSON.parse(r.rulesJson||'[]').length } catch { return 0 } })() - if (!map[r.siteType] || r.version > map[r.siteType].version) - map[r.siteType] = { ruleCount: cnt, version: r.version, updatedAt: r.updatedAt, rulesJson: r.rulesJson } + const key = r.siteId || r.siteType // siteId is the new key, fallback to siteType + if (!map[key] || r.version > map[key].version) + map[key] = { ruleCount: cnt, version: r.version, updatedAt: r.updatedAt, rulesJson: r.rulesJson } } rulesMap.value = map } finally { loading.value = false } } const editRule = async (row) => { - editType.value = row.siteType + editType.value = row.siteId try { - const r = await api.raw.get('/admin/risk-rules/'+row.siteType) + const r = await api.raw.get('/admin/risk-rules/'+row.siteId) editorJson.value = (r?.data||r)?.rules ? JSON.stringify((r.data||r).rules, null, 2) : '[]' } catch { editorJson.value = '[]' } }