177 lines
5.3 KiB
PHP
177 lines
5.3 KiB
PHP
<?php
|
||
|
||
namespace app\common;
|
||
|
||
use think\Db;
|
||
|
||
/**
|
||
* AI 写作模版句规则匹配(t_ai_template_sentence)
|
||
*/
|
||
class AiTemplateSentenceService
|
||
{
|
||
/** @var array<int,array>|null */
|
||
private static $ruleCache;
|
||
|
||
/**
|
||
* 按稿件分节统计模版句出现次数
|
||
*
|
||
* @param array<string,string> $sectionTexts 键:abstract/introduction/methods/results/discussion/conclusion
|
||
* @return array{total_matches:int,matched_rule_count:int,items:array,by_section:array}
|
||
*/
|
||
public function countInManuscript(array $sectionTexts): array
|
||
{
|
||
$rules = $this->loadActiveRules();
|
||
if (empty($rules)) {
|
||
return [
|
||
'total_matches' => 0,
|
||
'matched_rule_count' => 0,
|
||
'items' => [],
|
||
'by_section' => [],
|
||
];
|
||
}
|
||
|
||
$normalizedSections = [];
|
||
foreach ($sectionTexts as $key => $text) {
|
||
$sectionKey = strtolower(trim((string) $key));
|
||
if ($sectionKey === '') {
|
||
continue;
|
||
}
|
||
$normalizedSections[$sectionKey] = $this->normalizeText((string) $text);
|
||
}
|
||
|
||
$allText = $this->normalizeText(implode("\n\n", array_filter($normalizedSections)));
|
||
|
||
$items = [];
|
||
$bySection = [];
|
||
|
||
foreach ($rules as $rule) {
|
||
$ruleSection = strtolower(trim((string) ($rule['section'] ?? '')));
|
||
$pattern = trim((string) ($rule['sentence_pattern'] ?? ''));
|
||
if ($pattern === '') {
|
||
continue;
|
||
}
|
||
|
||
$targetText = '';
|
||
if ($ruleSection === 'all') {
|
||
$targetText = $allText;
|
||
} elseif (isset($normalizedSections[$ruleSection])) {
|
||
$targetText = $normalizedSections[$ruleSection];
|
||
} else {
|
||
continue;
|
||
}
|
||
|
||
if ($targetText === '') {
|
||
continue;
|
||
}
|
||
|
||
$count = $this->countPatternMatches($targetText, $pattern);
|
||
if ($count <= 0) {
|
||
continue;
|
||
}
|
||
|
||
$item = [
|
||
'id' => intval($rule['id'] ?? 0),
|
||
'section' => $ruleSection,
|
||
'sentence_pattern' => $pattern,
|
||
'count' => $count,
|
||
'risk_level' => intval($rule['risk_level'] ?? 0),
|
||
'weight' => intval($rule['weight'] ?? 0),
|
||
'remark' => (string) ($rule['remark'] ?? ''),
|
||
];
|
||
$items[] = $item;
|
||
|
||
if (!isset($bySection[$ruleSection])) {
|
||
$bySection[$ruleSection] = [
|
||
'total_matches' => 0,
|
||
'matched_rule_count' => 0,
|
||
'items' => [],
|
||
];
|
||
}
|
||
$bySection[$ruleSection]['items'][] = $item;
|
||
$bySection[$ruleSection]['total_matches'] += $count;
|
||
$bySection[$ruleSection]['matched_rule_count']++;
|
||
}
|
||
|
||
usort($items, function ($a, $b) {
|
||
if ($a['count'] !== $b['count']) {
|
||
return $b['count'] - $a['count'];
|
||
}
|
||
if ($a['weight'] !== $b['weight']) {
|
||
return $b['weight'] - $a['weight'];
|
||
}
|
||
return $a['id'] - $b['id'];
|
||
});
|
||
|
||
foreach ($bySection as &$sectionStats) {
|
||
usort($sectionStats['items'], function ($a, $b) {
|
||
if ($a['count'] !== $b['count']) {
|
||
return $b['count'] - $a['count'];
|
||
}
|
||
return $b['weight'] - $a['weight'];
|
||
});
|
||
}
|
||
unset($sectionStats);
|
||
|
||
$totalMatches = 0;
|
||
foreach ($items as $item) {
|
||
$totalMatches += intval($item['count']);
|
||
}
|
||
|
||
return [
|
||
'total_matches' => $totalMatches,
|
||
'matched_rule_count' => count($items),
|
||
'items' => $items,
|
||
'by_section' => $bySection,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* @return array<int,array>
|
||
*/
|
||
private function loadActiveRules(): array
|
||
{
|
||
if (self::$ruleCache !== null) {
|
||
return self::$ruleCache;
|
||
}
|
||
|
||
try {
|
||
self::$ruleCache = Db::name('ai_template_sentence')
|
||
->where('status', 1)
|
||
->field('id,sentence_pattern,section,language,risk_level,weight,remark,article_type,version')
|
||
->order('section asc,id asc')
|
||
->select();
|
||
} catch (\Throwable $e) {
|
||
self::$ruleCache = [];
|
||
}
|
||
|
||
return self::$ruleCache;
|
||
}
|
||
|
||
private function normalizeText(string $text): string
|
||
{
|
||
return ManuscriptTextCleanService::clean($text);
|
||
}
|
||
|
||
private function countPatternMatches(string $text, string $pattern): int
|
||
{
|
||
if ($text === '' || $pattern === '') {
|
||
return 0;
|
||
}
|
||
|
||
$regex = '#' . $pattern . '#iu';
|
||
$error = null;
|
||
set_error_handler(function ($errno, $errstr) use (&$error) {
|
||
$error = $errstr;
|
||
return true;
|
||
});
|
||
$count = preg_match_all($regex, $text, $matches);
|
||
restore_error_handler();
|
||
|
||
if ($count === false || $error !== null) {
|
||
return 0;
|
||
}
|
||
|
||
return intval($count);
|
||
}
|
||
}
|