This block of classes handles the display and processing of the form used to create/edit a Campaign
entity via the UI.
It follows the standard architecture used in timveroOS: Form → FormService → Entity , with full versioning support through HistoryEntityFormService
.
CampaignForm
is a DTO class representing the fields of the campaign creation/edit form.
Validation annotations provide basic input checks.
Example fields:
Copy public class CampaignForm {
@NotEmpty
private String name;
@NotNull
private CampaignExecutionType executionType;
private boolean restartable;
private Integer restartableUnit;
private RestartableUnitType restartableUnitType;
@Valid
private ExpressionForm expression;
@NotEmpty
private Set<@NotNull String> creditProductCodes;
private LocalDateTime dateTimeExecution;
// getters and setters
}
The CampaignFormMapper
interface is used to convert between Campaign
and CampaignForm
using the MapStruct library.
It extends EntityToFormMapper<Campaign, CampaignForm>
.
Example:
Copy @Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = ReferenceMapper.class)
public interface CampaignFormMapper extends EntityToFormMapper<Campaign, CampaignForm> {
}
MapStruct automatically generates the conversion based on matching fields.
Additional logic can be added using @AfterMapping
if needed.
⚙️ CampaignFormService — Extends HistoryEntityFormService
CampaignFormService
implements the core business logic for editing campaigns via the UI.
It extends HistoryEntityFormService<Campaign, CampaignForm, Long>
, which means:
Versioning is handled automatically — saving a campaign creates a new version
Previous versions are marked as inactive
The version lineage is tracked via lineageId
Example:
Copy @Service
public class CampaignFormService extends
HistoryEntityFormService<Campaign, CampaignForm, Long> {
@Autowired
private CreditProductRepository productRepository;
@Autowired
private ScriptManager scriptManager;
@Override
protected void assembleEditModel(Long aLong, CampaignForm form, Map<String, Object> model) {
model.put("campaign", form);
model.put("executionTypes", CampaignExecutionType.values());
model.put("creditProductCodes", productRepository.getCreditProductCodes());
model.put("restartableUnitTypes", RestartableUnitType.values());
model.put("engineNames", scriptManager.getEngineNames());
}
@Override
protected void assembleViewModel(Long id, Campaign entity, Map<String, Object> model) {
// Implementation as needed
}
@Override
public List<Campaign> findAllVersions(UUID versionId) {
return super.findAllVersions(versionId);
}
}
Key Method:
Copy @Override
protected void assembleEditModel(Long aLong, CampaignForm form, Map<String, Object> model) {
model.put("campaign", form);
model.put("executionTypes", CampaignExecutionType.values());
model.put("creditProductCodes", productRepository.getCreditProductCodes());
model.put("restartableUnitTypes", RestartableUnitType.values());
model.put("engineNames", scriptManager.getEngineNames());
}
Version History Support:
Copy @Override
public List<Campaign> findAllVersions(UUID versionId) {
return super.findAllVersions(versionId);
}
Dependencies Used:
CreditProductRepository
— for retrieving available credit products
ScriptManager
— for showing available scripting engines
✅ /campaign/list.html
Copy <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<th:block>
<table class="table">
<thead>
<tr role="row">
<th data-sortField="id" th:text="#{symbol.id}"></th>
<th data-sortField="name" th:text="#{campaign.name}"></th>
<th th:text="#{campaign.active}"></th>
<th th:text="#{campaign.executionType}"></th>
<th th:text="#{campaign.lastStartDate}"></th>
</tr>
</thead>
<tbody th:attr="data-page=${page.page},data-total=${page.total}">
<th:block
th:each="entity : ${page.rows}"
th:object="${entity}">
<tr role="row"
class="clickable"
th:href="@{'/campaign/'+ ${entity.id}}">
<td th:text="*{id}"></td>
<td th:text="*{name}"></td>
<td th:text="*{active} ? #{common.yes} : #{common.no} "></td>
<td th:text="*{#enums.name(executionType)}"></td>
<td th:text="${latestExecution} ? ${#ldates.format(latestExecution.createdAt)} : #{campaign.noneStartDate}"></td>
</tr>
</th:block>
</tbody>
</table>
</th:block>
</html>
Purpose:
Displays a table with the list of all campaigns. It serves as the main campaign overview page.
Key Features:
Sortable by id
, name
, and executionType
Displays active/inactive status
Shows the last execution date (if available)