You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digital-rmb-backend/src/main/java/com/yau/digitalrmb/training/application/TrainingTaskProgressService...

133 lines
7.4 KiB
Java

package com.yau.digitalrmb.training.application;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Service
public class TrainingTaskProgressService {
private static final String GENERATION = "PBOC_CURRENCY_GENERATION";
private static final String ISSUANCE = "PBOC_CURRENCY_ISSUANCE";
private static final String WALLET = "WALLET_OPENING";
private static final String EXCHANGE = "CURRENCY_EXCHANGE";
private static final String PAYMENT = "CURRENCY_PAYMENT";
private final JdbcTemplate jdbc;
private final Map<String, ModuleDefinition> definitions;
public TrainingTaskProgressService(JdbcTemplate jdbc) {
this.jdbc = jdbc;
this.definitions = definitions();
}
public List<TrainingTaskView> all(String userId) {
List<TrainingTaskView> result = new ArrayList<>();
for (String moduleCode : definitions.keySet()) result.add(one(userId, moduleCode));
return result;
}
public TrainingTaskView one(String userId, String moduleCode) {
ModuleDefinition module = definitions.get(moduleCode);
if (module == null) throw new IllegalArgumentException("不支持的实训模块:" + moduleCode);
List<String> completed = jdbc.queryForList("SELECT step_code FROM training_task_step_progress WHERE user_id = ? AND module_code = ?",
String.class, userId, moduleCode);
return view(module, completed);
}
/** 真实业务步骤成功后调用;同步补齐此前步骤,避免历史业务数据导致进度断层。 */
public void completeThrough(String userId, String moduleCode, String stepCode) {
ModuleDefinition module = definitions.get(moduleCode);
if (module == null || module.unavailable) return;
int index = module.indexOf(stepCode);
if (index < 0) return;
Instant now = Instant.now();
for (int i = 0; i <= index; i++) {
String currentStep = module.steps.get(i).code;
jdbc.update("INSERT INTO training_task_step_progress (user_id, module_code, step_code, completed_at) VALUES (?, ?, ?, ?) "
+ "ON DUPLICATE KEY UPDATE completed_at = completed_at",
userId, moduleCode, currentStep, now);
}
}
public void reset(String userId, String moduleCode) {
jdbc.update("DELETE FROM training_task_step_progress WHERE user_id = ? AND module_code = ?", userId, moduleCode);
}
private TrainingTaskView view(ModuleDefinition module, List<String> completed) {
List<TrainingStepView> steps = new ArrayList<>();
boolean prefixCompleted = true;
int completedCount = 0;
String currentStepCode = null;
for (StepDefinition step : module.steps) {
boolean done = prefixCompleted && completed.contains(step.code);
String status;
boolean actionAvailable = false;
if (done) {
status = "COMPLETED";
completedCount++;
} else if (module.unavailable && completedCount == 0) {
status = "NOT_STARTED";
prefixCompleted = false;
} else if (prefixCompleted) {
status = "CURRENT";
actionAvailable = true;
currentStepCode = step.code;
prefixCompleted = false;
} else {
status = "LOCKED";
}
steps.add(new TrainingStepView(step.code, step.name, step.sortNo, status, actionAvailable));
}
String status = completedCount == module.steps.size() ? "COMPLETED" : completedCount == 0 ? "NOT_STARTED" : "IN_PROGRESS";
return new TrainingTaskView(module.code, module.name, status, module.steps.size(), completedCount, currentStepCode,
module.unavailable, module.unavailableReason, steps);
}
private Map<String, ModuleDefinition> definitions() {
Map<String, ModuleDefinition> result = new LinkedHashMap<>();
result.put(GENERATION, module(GENERATION, "货币生成", false, null,
"机构身份信息与密钥准备", "生成数字货币请求", "验证货币生成请求", "生成交易信息标识", "生成额度控制位请求", "验证额度控制位请求", "控制系统签名", "生成额度控制位", "生成标准面额币串"));
result.put(ISSUANCE, module(ISSUANCE, "货币发行", false, null,
"查看货币需求", "生成发行申请计划", "发行计划数字签名", "发送标准报文", "中央银行验证与业务核查", "准备金通知与ACS扣减", "发行并确权数字货币"));
result.put(WALLET, module(WALLET, "开立数字钱包", true, "钱包开立真实业务接口尚未实现",
"开立数字钱包"));
result.put(EXCHANGE, module(EXCHANGE, "货币兑换", false, null,
"确认银行卡、钱包和商业银行库存", "生成取币请求报文", "钱包私钥签名", "商业银行验签并扣款", "央行确认币串权属"));
result.put(PAYMENT, module(PAYMENT, "数字货币支付", false, null,
"初始化付款方和收款方钱包", "签名支付请求", "付款行预处理", "中央银行结算", "收款行入账", "生成支付回执与到账通知"));
return Collections.unmodifiableMap(result);
}
private ModuleDefinition module(String code, String name, boolean unavailable, String reason, String... stepNames) {
List<StepDefinition> steps = new ArrayList<>();
for (int i = 0; i < stepNames.length; i++) steps.add(new StepDefinition(String.format("%02d", i + 1), stepNames[i], (i + 1) * 10));
return new ModuleDefinition(code, name, unavailable, reason, steps);
}
@Getter @AllArgsConstructor
public static class TrainingTaskView {
private final String moduleCode; private final String moduleName; private final String status;
private final int totalSteps; private final int completedSteps; private final String currentStepCode;
private final boolean actionUnavailable; private final String unavailableReason; private final List<TrainingStepView> steps;
}
@Getter @AllArgsConstructor
public static class TrainingStepView {
private final String stepCode; private final String stepName; private final int sortNo; private final String status; private final boolean actionAvailable;
}
private static class ModuleDefinition {
private final String code, name; private final boolean unavailable; private final String unavailableReason; private final List<StepDefinition> steps;
private ModuleDefinition(String code, String name, boolean unavailable, String unavailableReason, List<StepDefinition> steps) { this.code = code; this.name = name; this.unavailable = unavailable; this.unavailableReason = unavailableReason; this.steps = steps; }
private int indexOf(String code) { for (int i = 0; i < steps.size(); i++) if (steps.get(i).code.equals(code)) return i; return -1; }
}
private static class StepDefinition { private final String code, name; private final int sortNo; private StepDefinition(String code, String name, int sortNo) { this.code = code; this.name = name; this.sortNo = sortNo; } }
}