1
0

feat: 增加优化器模板的编辑

This commit is contained in:
2024-12-23 23:35:21 +08:00
parent 4204a3e282
commit 1c868815de
4 changed files with 287 additions and 92 deletions

View File

@@ -108,3 +108,21 @@ class Line(
interface LineRepository : JpaRepository<Line, String>, JpaSpecificationExecutor<Line> { interface LineRepository : JpaRepository<Line, String>, JpaSpecificationExecutor<Line> {
fun findAllByChapter_ChapterId(chapterId: String, sort: Sort): List<Line> fun findAllByChapter_ChapterId(chapterId: String, sort: Sort): List<Line>
} }
@Entity
@DynamicUpdate
class OptimizationTemplate(
@Id
var templateId: String,
@Column(nullable = false)
var name: String,
var description: String?,
@Lob
@Basic(fetch = FetchType.LAZY)
@Column(nullable = false, length = Int.MAX_VALUE)
var config: String,
)
@Repository
interface OptimizationTemplateRepository : JpaRepository<OptimizationTemplate, String>,
JpaSpecificationExecutor<OptimizationTemplate>

View File

@@ -1,27 +1,35 @@
package com.lanyuanxiaoyao.bookstore.controller package com.lanyuanxiaoyao.bookstore.controller
import cn.hutool.core.util.IdUtil
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.lanyuanxiaoyao.bookstore.CharWidthOptimization import com.lanyuanxiaoyao.bookstore.CharWidthOptimization
import com.lanyuanxiaoyao.bookstore.ListResponse import com.lanyuanxiaoyao.bookstore.ListResponse
import com.lanyuanxiaoyao.bookstore.Optimization import com.lanyuanxiaoyao.bookstore.Optimization
import com.lanyuanxiaoyao.bookstore.Optimization.ArgumentType.LIST import com.lanyuanxiaoyao.bookstore.OptimizationTemplate
import com.lanyuanxiaoyao.bookstore.Optimization.ArgumentType.MAP import com.lanyuanxiaoyao.bookstore.OptimizationTemplateRepository
import com.lanyuanxiaoyao.bookstore.Optimization.ArgumentType.NONE import com.lanyuanxiaoyao.bookstore.PageResponse
import com.lanyuanxiaoyao.bookstore.SingleResponse import com.lanyuanxiaoyao.bookstore.SingleResponse
import com.lanyuanxiaoyao.bookstore.SliceOptimization import com.lanyuanxiaoyao.bookstore.SliceOptimization
import com.lanyuanxiaoyao.bookstore.TrimOptimization import com.lanyuanxiaoyao.bookstore.TrimOptimization
import java.awt.SystemColor.text import jakarta.transaction.Transactional
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.data.domain.PageRequest
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
@RestController @RestController
@RequestMapping("optimization") @RequestMapping("optimization")
class OptimizationController(builder: Jackson2ObjectMapperBuilder) { class OptimizationController(
private val optimizationTemplateRepository: OptimizationTemplateRepository,
builder: Jackson2ObjectMapperBuilder,
) {
private val log = LoggerFactory.getLogger(javaClass) private val log = LoggerFactory.getLogger(javaClass)
private val mapper: ObjectMapper = builder.build() private val mapper: ObjectMapper = builder.build()
@@ -43,4 +51,60 @@ class OptimizationController(builder: Jackson2ObjectMapperBuilder) {
val lines = Optimization.process(root, optimizationMap) val lines = Optimization.process(root, optimizationMap)
return SingleResponse(lines.joinToString("\n")) return SingleResponse(lines.joinToString("\n"))
} }
@GetMapping("list")
fun list(
@RequestParam("page", defaultValue = "1") page: Int,
@RequestParam("size", defaultValue = "10") size: Int,
): PageResponse<ViewItem> {
val pageable = optimizationTemplateRepository.findAll(PageRequest.of(0.coerceAtLeast(page - 1), size))
return PageResponse(
pageable.content.map { template ->
ViewItem(
templateId = template.templateId,
name = template.name,
description = template.description,
config = mapper.readTree(template.config),
)
},
pageable.totalElements,
)
}
@Transactional
@PostMapping("save")
fun save(@RequestBody item: ViewItem) {
optimizationTemplateRepository.save(
OptimizationTemplate(
templateId = item.templateId ?: IdUtil.simpleUUID(),
name = item.name,
description = item.description,
config = mapper.writeValueAsString(item.config),
)
)
}
@GetMapping("detail/{templateId}")
fun detail(@PathVariable("templateId") templateId: String): ViewItem {
val template = optimizationTemplateRepository.findById(templateId).orElseThrow()
return ViewItem(
templateId = template.templateId,
name = template.name,
description = template.description,
config = mapper.readTree(template.config),
)
}
@Transactional
@GetMapping("remove/{templateId}")
fun remove(@PathVariable("templateId") templateId: String) {
optimizationTemplateRepository.deleteById(templateId)
}
data class ViewItem(
val templateId: String?,
val name: String,
val description: String?,
val config: JsonNode,
)
} }

View File

@@ -0,0 +1,143 @@
function optimizationTemplateForm() {
return {
debug: true,
type: 'form',
name: 'optimization-template-form',
...horizontalFormOptions(),
body: [
{
type: 'hidden',
name: 'templateId',
},
{
type: 'input-text',
name: 'name',
label: '名称',
required: true,
...formInputClearable(),
},
{
type: 'textarea',
name: 'description',
label: '简介',
...formInputClearable(),
showCounter: true,
trimContents: true,
minRows: 2,
maxRows: 2,
maxLength: 100,
},
{
type: 'combo',
name: 'config',
label: '优化器',
required: true,
multiLine: true,
multiple: true,
draggable: true,
items: [
{
name: 'type',
label: '类型',
type: 'select',
required: true,
selectFirst: true,
source: '${base}/optimization/optimizations',
},
{
visibleOn: "${ENDSWITH(type, 'MAP')}",
type: 'input-kv',
name: 'argument',
label: '参数',
required: true,
},
{
visibleOn: "${ENDSWITH(type, 'LIST')}",
type: 'combo',
name: 'argument',
label: '参数',
required: true,
multiple: true,
multiLine: true,
flat: true,
items: [
{
type: 'input-text',
name: 'argument',
label: '目标值',
required: true,
}
]
},
]
},
{
type: 'textarea',
name: 'text',
label: '测试正文',
...formInputClearable(),
showCounter: true,
trimContents: true,
minRows: 5,
maxRows: 5,
},
{
type: 'action',
label: '优化预览',
className: 'w-full mb-5',
level: 'primary',
actionType: 'ajax',
api: {
method: 'post',
url: '${base}/optimization/execute',
data: {
text: '${text|default:undefined}',
optimizations: '${config|default:undefined}'
}
},
reload: 'optimization-template-form?newText=${item}'
},
{
disabled: true,
type: 'diff-editor',
name: 'newText',
label: '正文优化',
diffValue: '${text}',
language: 'plain-text'
},
],
}
}
function optimizationTemplateAddDialog() {
return {
type: 'action',
actionType: 'dialog',
dialog: {
title: '新增优化器模板',
size: 'lg',
body: {
...optimizationTemplateForm(),
api: '${base}/optimization/save',
},
},
}
}
function optimizationTemplateDetailDialog() {
return {
type: 'action',
actionType: 'dialog',
dialog: {
title: '编辑优化器模板',
size: 'lg',
body: [
{
...optimizationTemplateForm(),
initApi: '${base}/optimization/detail/${templateId}',
api: '${base}/optimization/save',
},
],
},
}
}

View File

@@ -38,6 +38,7 @@
<script src="components/helper.js"></script> <script src="components/helper.js"></script>
<script src="components/book.js"></script> <script src="components/book.js"></script>
<script src="components/chapter.js"></script> <script src="components/chapter.js"></script>
<script src="components/optimization.js"></script>
<script> <script>
(function () { (function () {
let amis = amisRequire('amis/embed') let amis = amisRequire('amis/embed')
@@ -59,8 +60,8 @@
url: '${base}/book/list', url: '${base}/book/list',
data: { data: {
page: '${page|default:1}', page: '${page|default:1}',
size: '${size|default:10}' size: '${size|default:10}',
} },
}, },
...crudCommonOptions(), ...crudCommonOptions(),
headerToolbar: [ headerToolbar: [
@@ -122,99 +123,68 @@
}, },
], ],
}, },
] ],
}, },
{ {
title: '优化', title: '优化',
body: [ body: [
{ {
type: 'form', type: 'crud',
name: 'optimization-form', api: {
body: [ method: 'get',
url: '${base}/optimization/list',
data: {
page: '${page|default:1}',
size: '${size|default:10}',
},
},
...crudCommonOptions(),
headerToolbar: [
'reload',
{ {
type: 'textarea', label: '',
name: 'text', icon: 'fa fa-add',
label: '正文', ...optimizationTemplateAddDialog(),
required: true, },
...formInputClearable(), ],
showCounter: true, columns: [
trimContents: true, {
minRows: 5, name: 'name',
maxRows: 5, label: '名称',
width: 150,
}, },
{ {
type: 'combo', name: 'description',
name: 'optimizations', label: '描述',
label: '优化器',
required: true,
multiLine: true,
multiple: true,
draggable: true,
items: [
{
name: 'type',
label: '类型',
type: 'select',
required: true,
selectFirst: true,
source: '${base}/optimization/optimizations',
}, },
{ {
visibleOn: "${ENDSWITH(type, 'MAP')}", type: 'operation',
type: 'input-kv', label: '操作',
name: 'argument', fixed: 'right',
label: '参数', className: 'nowrap',
required: true, width: 100,
}, buttons: [
{ {
visibleOn: "${ENDSWITH(type, 'LIST')}", type: 'action',
type: 'combo', label: '编辑',
name: 'argument', ...optimizationTemplateDetailDialog(),
label: '参数',
required: true,
multiple: true,
multiLine: true,
flat: true,
items: [
{
type: 'input-text',
name: 'argument',
label: '目标值',
required: true,
}
]
},
]
}, },
{ {
type: 'action', type: 'action',
label: '执行优化', label: '删除',
className: 'w-full mb-5', level: 'danger',
level: 'primary', confirmTitle: '确认删除',
confirmText: '确认删除名称为「${name}」的优化器模板吗?',
actionType: 'ajax', actionType: 'ajax',
api: { api: 'get:${base}/optimization/remove/${templateId}',
method: 'post',
url: '${base}/optimization/execute',
data: {
text: '${text|default:undefined}',
optimizations: '${optimizations|default:undefined}'
}
},
reload: 'optimization-form?newText=${item}'
},
{
disabled: true,
type: 'diff-editor',
name: 'newText',
label: '正文优化',
diffValue: '${text}',
language: 'plain-text'
}, },
] ]
}, },
] ],
} },
] ],
},
],
}, },
} }
amis.embed( amis.embed(
@@ -222,7 +192,7 @@
amisJSON, amisJSON,
{ {
data: { data: {
...information ...information,
}, },
}, },
{ {