fix: store risk rules per siteId instead of shared siteType

This commit is contained in:
mediabot-pt
2026-07-06 23:50:23 +08:00
parent dde14cb73f
commit 5a7b218944
4 changed files with 47 additions and 21 deletions

View File

@@ -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;

View File

@@ -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<RiskRule>()
.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<RiskRule>()
.eq(RiskRule::getSiteType, identifier).eq(RiskRule::getDeleted, 0)
.orderByDesc(RiskRule::getId).last("limit 1"));
}
@Override
public Map<String, Object> getRules(String siteType) {
RiskRule rr = mapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<RiskRule>()
.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<Map<String, Object>> 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<RiskRule>()
.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<Map<String, Object>> listVersions(String siteType) {
var list = mapper.selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<RiskRule>()
.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<RiskRule>()
.eq(RiskRule::getSiteType, siteType).eq(RiskRule::getDeleted, 0)
.orderByDesc(RiskRule::getId));
}
List<Map<String, Object>> 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<RiskRule>()
.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<RiskRule>()
.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<RiskRule>()
.eq(RiskRule::getSiteId, siteType).eq(RiskRule::getDeleted, 0));
mapper.delete(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<RiskRule>()
.eq(RiskRule::getSiteType, siteType).eq(RiskRule::getDeleted, 0));
}
@@ -108,6 +127,7 @@ public class RiskRuleServiceImpl implements RiskRuleService {
Map<String, Object> 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);

View File

@@ -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);

View File

@@ -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 = '[]' }
}