wwh недель назад: 3
Родитель
Сommit
2926e32996

+ 3 - 1
huimv-employment/fe-api/src/main/java/com/huimv/employment/controller/mcp/McpDraftController.java

@@ -77,7 +77,9 @@ public class McpDraftController {
77
 
77
 
78
     @PostMapping("/calculate")
78
     @PostMapping("/calculate")
79
     @Operation(summary = "成本测算",
79
     @Operation(summary = "成本测算",
80
-            description = "草稿 status=ready 后调用,返回 SCHEME_A/SCHEME_B 多套合规方案。",
80
+            description = "当前会话草稿就绪后调用。"
81
+                    + "按 fe_service_plan(status=active,排除 current)及 fe_service_plan_fee_rule 预置规则测算,"
82
+                    + "返回成本方案列表(如 SCHEME_A / SCHEME_B),并落库 fe_draft_cost_snapshot。",
81
             parameters = {
83
             parameters = {
82
                     @Parameter(name = "X-Fe-Ai-Key", in = ParameterIn.HEADER)
84
                     @Parameter(name = "X-Fe-Ai-Key", in = ParameterIn.HEADER)
83
             })
85
             })

+ 3 - 1
huimv-employment/fe-api/src/main/java/com/huimv/employment/controller/mp/DraftController.java

@@ -63,7 +63,9 @@ public class DraftController {
63
 
63
 
64
     @GetMapping("/{id}/cost-comparison")
64
     @GetMapping("/{id}/cost-comparison")
65
     @Operation(summary = "成本测算(方案对比)",
65
     @Operation(summary = "成本测算(方案对比)",
66
-            description = "要求草稿 pending 且 missingFields 为空。返回 SCHEME_A / SCHEME_B 等多套合规方案,供「确认此方案」前展示。")
66
+            description = "要求草稿 pending 且 missingFields 为空。"
67
+                    + "按 fe_service_plan(status=active,排除 current)及 fe_service_plan_fee_rule 预置规则测算,"
68
+                    + "返回成本方案列表(如 SCHEME_A / SCHEME_B),并落库 fe_draft_cost_snapshot。")
67
     public R<McpDraftCalculateResponse> costComparison(@PathVariable("id") Long draftId) {
69
     public R<McpDraftCalculateResponse> costComparison(@PathVariable("id") Long draftId) {
68
         LoginUser loginUser = requireEnterprise();
70
         LoginUser loginUser = requireEnterprise();
69
         return R.ok(draftService.calculateCostComparison(loginUser.getUserId(), draftId));
71
         return R.ok(draftService.calculateCostComparison(loginUser.getUserId(), draftId));

+ 194 - 0
huimv-employment/fe-dao/src/main/java/com/huimv/employment/dao/entity/FeServicePlan.java

@@ -0,0 +1,194 @@
1
+package com.huimv.employment.dao.entity;
2
+
3
+import com.baomidou.mybatisplus.annotation.IdType;
4
+import com.baomidou.mybatisplus.annotation.TableId;
5
+import com.baomidou.mybatisplus.annotation.TableLogic;
6
+import com.baomidou.mybatisplus.annotation.TableName;
7
+
8
+import java.math.BigDecimal;
9
+import java.time.LocalDate;
10
+import java.time.LocalDateTime;
11
+
12
+/**
13
+ * 用工方案目录 {@code fe_service_plan} 实体。
14
+ */
15
+@TableName("fe_service_plan")
16
+public class FeServicePlan {
17
+
18
+    @TableId(type = IdType.AUTO)
19
+    private Long id;
20
+
21
+    private String planCode;
22
+
23
+    private String planName;
24
+
25
+    /** flexible / traditional / hybrid */
26
+    private String planType;
27
+
28
+    private String description;
29
+
30
+    private BigDecimal serviceFeeRate;
31
+
32
+    /** basic / standard / premium */
33
+    private String insuranceLevel;
34
+
35
+    private String taxMode;
36
+
37
+    /** JSON 数组字符串 */
38
+    private String applicableScenarios;
39
+
40
+    /** JSON 数组字符串 */
41
+    private String complianceTags;
42
+
43
+    private Integer sortOrder;
44
+
45
+    /** active / test_only / disabled */
46
+    private String status;
47
+
48
+    private LocalDate effectiveFrom;
49
+
50
+    private LocalDate effectiveTo;
51
+
52
+    private LocalDateTime createTime;
53
+
54
+    private LocalDateTime updateTime;
55
+
56
+    @TableLogic
57
+    private Integer delFlag;
58
+
59
+    public Long getId() {
60
+        return id;
61
+    }
62
+
63
+    public void setId(Long id) {
64
+        this.id = id;
65
+    }
66
+
67
+    public String getPlanCode() {
68
+        return planCode;
69
+    }
70
+
71
+    public void setPlanCode(String planCode) {
72
+        this.planCode = planCode;
73
+    }
74
+
75
+    public String getPlanName() {
76
+        return planName;
77
+    }
78
+
79
+    public void setPlanName(String planName) {
80
+        this.planName = planName;
81
+    }
82
+
83
+    public String getPlanType() {
84
+        return planType;
85
+    }
86
+
87
+    public void setPlanType(String planType) {
88
+        this.planType = planType;
89
+    }
90
+
91
+    public String getDescription() {
92
+        return description;
93
+    }
94
+
95
+    public void setDescription(String description) {
96
+        this.description = description;
97
+    }
98
+
99
+    public BigDecimal getServiceFeeRate() {
100
+        return serviceFeeRate;
101
+    }
102
+
103
+    public void setServiceFeeRate(BigDecimal serviceFeeRate) {
104
+        this.serviceFeeRate = serviceFeeRate;
105
+    }
106
+
107
+    public String getInsuranceLevel() {
108
+        return insuranceLevel;
109
+    }
110
+
111
+    public void setInsuranceLevel(String insuranceLevel) {
112
+        this.insuranceLevel = insuranceLevel;
113
+    }
114
+
115
+    public String getTaxMode() {
116
+        return taxMode;
117
+    }
118
+
119
+    public void setTaxMode(String taxMode) {
120
+        this.taxMode = taxMode;
121
+    }
122
+
123
+    public String getApplicableScenarios() {
124
+        return applicableScenarios;
125
+    }
126
+
127
+    public void setApplicableScenarios(String applicableScenarios) {
128
+        this.applicableScenarios = applicableScenarios;
129
+    }
130
+
131
+    public String getComplianceTags() {
132
+        return complianceTags;
133
+    }
134
+
135
+    public void setComplianceTags(String complianceTags) {
136
+        this.complianceTags = complianceTags;
137
+    }
138
+
139
+    public Integer getSortOrder() {
140
+        return sortOrder;
141
+    }
142
+
143
+    public void setSortOrder(Integer sortOrder) {
144
+        this.sortOrder = sortOrder;
145
+    }
146
+
147
+    public String getStatus() {
148
+        return status;
149
+    }
150
+
151
+    public void setStatus(String status) {
152
+        this.status = status;
153
+    }
154
+
155
+    public LocalDate getEffectiveFrom() {
156
+        return effectiveFrom;
157
+    }
158
+
159
+    public void setEffectiveFrom(LocalDate effectiveFrom) {
160
+        this.effectiveFrom = effectiveFrom;
161
+    }
162
+
163
+    public LocalDate getEffectiveTo() {
164
+        return effectiveTo;
165
+    }
166
+
167
+    public void setEffectiveTo(LocalDate effectiveTo) {
168
+        this.effectiveTo = effectiveTo;
169
+    }
170
+
171
+    public LocalDateTime getCreateTime() {
172
+        return createTime;
173
+    }
174
+
175
+    public void setCreateTime(LocalDateTime createTime) {
176
+        this.createTime = createTime;
177
+    }
178
+
179
+    public LocalDateTime getUpdateTime() {
180
+        return updateTime;
181
+    }
182
+
183
+    public void setUpdateTime(LocalDateTime updateTime) {
184
+        this.updateTime = updateTime;
185
+    }
186
+
187
+    public Integer getDelFlag() {
188
+        return delFlag;
189
+    }
190
+
191
+    public void setDelFlag(Integer delFlag) {
192
+        this.delFlag = delFlag;
193
+    }
194
+}

+ 118 - 0
huimv-employment/fe-dao/src/main/java/com/huimv/employment/dao/entity/FeServicePlanFeeRule.java

@@ -0,0 +1,118 @@
1
+package com.huimv.employment.dao.entity;
2
+
3
+import com.baomidou.mybatisplus.annotation.IdType;
4
+import com.baomidou.mybatisplus.annotation.TableId;
5
+import com.baomidou.mybatisplus.annotation.TableName;
6
+
7
+import java.math.BigDecimal;
8
+import java.time.LocalDateTime;
9
+
10
+/**
11
+ * 方案费用项规则 {@code fe_service_plan_fee_rule} 实体。
12
+ */
13
+@TableName("fe_service_plan_fee_rule")
14
+public class FeServicePlanFeeRule {
15
+
16
+    @TableId(type = IdType.AUTO)
17
+    private Long id;
18
+
19
+    private Long planId;
20
+
21
+    /** labor_fee / service_fee / insurance / tax_withhold */
22
+    private String feeCode;
23
+
24
+    private String feeName;
25
+
26
+    /** fixed / rate / formula */
27
+    private String calcType;
28
+
29
+    private String calcExpression;
30
+
31
+    private BigDecimal defaultRate;
32
+
33
+    private Integer sortOrder;
34
+
35
+    private LocalDateTime createTime;
36
+
37
+    private LocalDateTime updateTime;
38
+
39
+    public Long getId() {
40
+        return id;
41
+    }
42
+
43
+    public void setId(Long id) {
44
+        this.id = id;
45
+    }
46
+
47
+    public Long getPlanId() {
48
+        return planId;
49
+    }
50
+
51
+    public void setPlanId(Long planId) {
52
+        this.planId = planId;
53
+    }
54
+
55
+    public String getFeeCode() {
56
+        return feeCode;
57
+    }
58
+
59
+    public void setFeeCode(String feeCode) {
60
+        this.feeCode = feeCode;
61
+    }
62
+
63
+    public String getFeeName() {
64
+        return feeName;
65
+    }
66
+
67
+    public void setFeeName(String feeName) {
68
+        this.feeName = feeName;
69
+    }
70
+
71
+    public String getCalcType() {
72
+        return calcType;
73
+    }
74
+
75
+    public void setCalcType(String calcType) {
76
+        this.calcType = calcType;
77
+    }
78
+
79
+    public String getCalcExpression() {
80
+        return calcExpression;
81
+    }
82
+
83
+    public void setCalcExpression(String calcExpression) {
84
+        this.calcExpression = calcExpression;
85
+    }
86
+
87
+    public BigDecimal getDefaultRate() {
88
+        return defaultRate;
89
+    }
90
+
91
+    public void setDefaultRate(BigDecimal defaultRate) {
92
+        this.defaultRate = defaultRate;
93
+    }
94
+
95
+    public Integer getSortOrder() {
96
+        return sortOrder;
97
+    }
98
+
99
+    public void setSortOrder(Integer sortOrder) {
100
+        this.sortOrder = sortOrder;
101
+    }
102
+
103
+    public LocalDateTime getCreateTime() {
104
+        return createTime;
105
+    }
106
+
107
+    public void setCreateTime(LocalDateTime createTime) {
108
+        this.createTime = createTime;
109
+    }
110
+
111
+    public LocalDateTime getUpdateTime() {
112
+        return updateTime;
113
+    }
114
+
115
+    public void setUpdateTime(LocalDateTime updateTime) {
116
+        this.updateTime = updateTime;
117
+    }
118
+}

+ 9 - 0
huimv-employment/fe-dao/src/main/java/com/huimv/employment/dao/mapper/FeServicePlanFeeRuleMapper.java

@@ -0,0 +1,9 @@
1
+package com.huimv.employment.dao.mapper;
2
+
3
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4
+import com.huimv.employment.dao.entity.FeServicePlanFeeRule;
5
+import org.apache.ibatis.annotations.Mapper;
6
+
7
+@Mapper
8
+public interface FeServicePlanFeeRuleMapper extends BaseMapper<FeServicePlanFeeRule> {
9
+}

+ 9 - 0
huimv-employment/fe-dao/src/main/java/com/huimv/employment/dao/mapper/FeServicePlanMapper.java

@@ -0,0 +1,9 @@
1
+package com.huimv.employment.dao.mapper;
2
+
3
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4
+import com.huimv.employment.dao.entity.FeServicePlan;
5
+import org.apache.ibatis.annotations.Mapper;
6
+
7
+@Mapper
8
+public interface FeServicePlanMapper extends BaseMapper<FeServicePlan> {
9
+}

+ 30 - 0
huimv-employment/fe-integration/src/main/java/com/huimv/employment/integration/kb/ConsoleChatCollectResult.java

@@ -0,0 +1,30 @@
1
+package com.huimv.employment.integration.kb;
2
+
3
+/**
4
+ * 会话落库场景:SSE 收集结果。
5
+ */
6
+public class ConsoleChatCollectResult {
7
+
8
+    private final String assistantText;
9
+    private final boolean completed;
10
+    /** 未转发的最后一条终端 SSE 原始行,可为 null */
11
+    private final String heldTerminalLine;
12
+
13
+    public ConsoleChatCollectResult(String assistantText, boolean completed, String heldTerminalLine) {
14
+        this.assistantText = assistantText == null ? "" : assistantText;
15
+        this.completed = completed;
16
+        this.heldTerminalLine = heldTerminalLine;
17
+    }
18
+
19
+    public String getAssistantText() {
20
+        return assistantText;
21
+    }
22
+
23
+    public boolean isCompleted() {
24
+        return completed;
25
+    }
26
+
27
+    public String getHeldTerminalLine() {
28
+        return heldTerminalLine;
29
+    }
30
+}

+ 240 - 36
huimv-employment/fe-service/src/main/java/com/huimv/employment/service/draft/DraftCostCalculationEngine.java

@@ -1,40 +1,115 @@
1
 package com.huimv.employment.service.draft;
1
 package com.huimv.employment.service.draft;
2
 
2
 
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.fasterxml.jackson.core.type.TypeReference;
5
+import com.fasterxml.jackson.databind.ObjectMapper;
6
+import com.huimv.employment.common.exception.BizException;
7
+import com.huimv.employment.common.exception.ErrorCode;
3
 import com.huimv.employment.dao.entity.FeEmploymentDraft;
8
 import com.huimv.employment.dao.entity.FeEmploymentDraft;
9
+import com.huimv.employment.dao.entity.FeServicePlan;
10
+import com.huimv.employment.dao.entity.FeServicePlanFeeRule;
11
+import com.huimv.employment.dao.mapper.FeServicePlanFeeRuleMapper;
12
+import com.huimv.employment.dao.mapper.FeServicePlanMapper;
4
 import com.huimv.employment.service.draft.dto.McpDraftCalculateResponse;
13
 import com.huimv.employment.service.draft.dto.McpDraftCalculateResponse;
5
 import com.huimv.employment.service.draft.dto.McpDraftSchemeCostResponse;
14
 import com.huimv.employment.service.draft.dto.McpDraftSchemeCostResponse;
6
 import com.huimv.employment.service.draft.dto.McpDraftSchemeResponse;
15
 import com.huimv.employment.service.draft.dto.McpDraftSchemeResponse;
7
 import com.huimv.employment.service.draft.dto.McpDraftSchemeWorkerIncomeResponse;
16
 import com.huimv.employment.service.draft.dto.McpDraftSchemeWorkerIncomeResponse;
8
 import org.springframework.stereotype.Component;
17
 import org.springframework.stereotype.Component;
18
+import org.springframework.util.StringUtils;
9
 
19
 
10
 import java.math.BigDecimal;
20
 import java.math.BigDecimal;
11
 import java.math.RoundingMode;
21
 import java.math.RoundingMode;
12
-import java.util.Arrays;
22
+import java.time.LocalDate;
23
+import java.util.ArrayList;
24
+import java.util.Collections;
25
+import java.util.HashMap;
13
 import java.util.List;
26
 import java.util.List;
27
+import java.util.Map;
28
+import java.util.regex.Matcher;
29
+import java.util.regex.Pattern;
14
 
30
 
15
 /**
31
 /**
16
- * 草稿成本测算引擎(v2 §6.3.4 黑盒)。
17
- * <p>一期内置 SCHEME_A(灵活用工)与 SCHEME_B(劳务报酬)两套规则,后续可接入 fe_service_plan。</p>
32
+ * 草稿成本测算引擎(v2 §6.3.4)。
33
+ * <p>从 {@code fe_service_plan} + {@code fe_service_plan_fee_rule} 读取方案与费率,不在代码中写死业务费率。</p>
18
  */
34
  */
19
 @Component
35
 @Component
20
 public class DraftCostCalculationEngine {
36
 public class DraftCostCalculationEngine {
21
 
37
 
22
-    public static final String SCHEME_A = "SCHEME_A";
23
-    public static final String SCHEME_B = "SCHEME_B";
24
     private static final String CALC_VERSION = "v1";
38
     private static final String CALC_VERSION = "v1";
39
+    private static final String STATUS_ACTIVE = "active";
40
+    private static final String PLAN_CURRENT = "current";
41
+    private static final String FEE_SERVICE = "service_fee";
42
+    private static final String FEE_TAX = "tax_withhold";
43
+    private static final String CALC_RATE = "rate";
44
+    private static final String CALC_FORMULA = "formula";
45
+    private static final String CALC_FIXED = "fixed";
25
 
46
 
26
-    /** 灵活用工平台服务费率 2% */
27
-    private static final BigDecimal SCHEME_A_SERVICE_FEE_RATE = new BigDecimal("0.02");
28
-    /** 劳务报酬预扣税率(示例:3000 元对应 440 元) */
29
-    private static final BigDecimal SCHEME_B_TAX_RATE = new BigDecimal("440").divide(new BigDecimal("3000"), 8, RoundingMode.HALF_UP);
47
+    /** gross*440/3000 或 gross * 0.02 */
48
+    private static final Pattern FORMULA_MUL_DIV = Pattern.compile(
49
+            "(?i)^(gross|labor|company_pay)\\s*\\*\\s*(\\d+(?:\\.\\d+)?)\\s*/\\s*(\\d+(?:\\.\\d+)?)$");
50
+    private static final Pattern FORMULA_MUL = Pattern.compile(
51
+            "(?i)^(gross|labor|company_pay)\\s*\\*\\s*(\\d+(?:\\.\\d+)?)$");
52
+
53
+    private final FeServicePlanMapper feServicePlanMapper;
54
+    private final FeServicePlanFeeRuleMapper feServicePlanFeeRuleMapper;
55
+    private final ObjectMapper objectMapper;
56
+
57
+    public DraftCostCalculationEngine(FeServicePlanMapper feServicePlanMapper,
58
+                                      FeServicePlanFeeRuleMapper feServicePlanFeeRuleMapper,
59
+                                      ObjectMapper objectMapper) {
60
+        this.feServicePlanMapper = feServicePlanMapper;
61
+        this.feServicePlanFeeRuleMapper = feServicePlanFeeRuleMapper;
62
+        this.objectMapper = objectMapper;
63
+    }
30
 
64
 
31
     public List<CalculatedScheme> calculate(FeEmploymentDraft draft) {
65
     public List<CalculatedScheme> calculate(FeEmploymentDraft draft) {
32
-        BigDecimal laborTotal = draft.getEstimatedTotal();
66
+        BigDecimal laborTotal = resolveLaborTotal(draft);
33
         int workerCount = draft.getWorkerCount() != null && draft.getWorkerCount() > 0
67
         int workerCount = draft.getWorkerCount() != null && draft.getWorkerCount() > 0
34
                 ? draft.getWorkerCount() : 1;
68
                 ? draft.getWorkerCount() : 1;
35
-        return Arrays.asList(
36
-                buildSchemeA(laborTotal, workerCount),
37
-                buildSchemeB(laborTotal, workerCount));
69
+
70
+        List<FeServicePlan> plans = listActiveComparisonPlans();
71
+        if (plans.isEmpty()) {
72
+            throw new BizException(ErrorCode.BAD_REQUEST, "未配置可用用工方案,请先初始化 fe_service_plan");
73
+        }
74
+
75
+        Map<Long, List<FeServicePlanFeeRule>> rulesByPlan = loadRulesByPlanIds(plans);
76
+        List<CalculatedScheme> result = new ArrayList<>(plans.size());
77
+        boolean recommendedAssigned = false;
78
+        for (FeServicePlan plan : plans) {
79
+            boolean recommended = !recommendedAssigned;
80
+            recommendedAssigned = true;
81
+            result.add(buildScheme(plan, rulesByPlan.getOrDefault(plan.getId(), Collections.emptyList()),
82
+                    laborTotal, workerCount, recommended));
83
+        }
84
+        return result;
85
+    }
86
+
87
+    /**
88
+     * 劳务费基数:优先人数×天数×日薪;否则用草稿 total_amount(estimated_total)。
89
+     */
90
+    private static BigDecimal resolveLaborTotal(FeEmploymentDraft draft) {
91
+        if (draft.getDailyWage() != null
92
+                && draft.getWorkerCount() != null && draft.getWorkerCount() > 0
93
+                && draft.getWorkDays() != null && draft.getWorkDays() > 0) {
94
+            return draft.getDailyWage()
95
+                    .multiply(BigDecimal.valueOf(draft.getWorkerCount()))
96
+                    .multiply(BigDecimal.valueOf(draft.getWorkDays()));
97
+        }
98
+        if (draft.getEstimatedTotal() != null
99
+                && draft.getEstimatedTotal().compareTo(BigDecimal.ZERO) > 0) {
100
+            return draft.getEstimatedTotal();
101
+        }
102
+        throw new BizException(ErrorCode.BAD_REQUEST, "草稿劳务费总额为空,无法测算(需 total_amount 或 人数×天数×日薪)");
103
+    }
104
+
105
+    /** 方案编码是否为库中 active 对比方案(非 current)。 */
106
+    public boolean isAllowedSchemeCode(String schemeCode) {
107
+        if (!StringUtils.hasText(schemeCode)) {
108
+            return false;
109
+        }
110
+        String code = schemeCode.trim();
111
+        return listActiveComparisonPlans().stream()
112
+                .anyMatch(p -> code.equalsIgnoreCase(p.getPlanCode()));
38
     }
113
     }
39
 
114
 
40
     public McpDraftCalculateResponse toApiResponse(List<CalculatedScheme> schemes) {
115
     public McpDraftCalculateResponse toApiResponse(List<CalculatedScheme> schemes) {
@@ -49,37 +124,159 @@ public class DraftCostCalculationEngine {
49
         return CALC_VERSION;
124
         return CALC_VERSION;
50
     }
125
     }
51
 
126
 
52
-    private CalculatedScheme buildSchemeA(BigDecimal laborTotal, int workerCount) {
127
+    private List<FeServicePlan> listActiveComparisonPlans() {
128
+        LocalDate today = LocalDate.now();
129
+        List<FeServicePlan> plans = feServicePlanMapper.selectList(new LambdaQueryWrapper<FeServicePlan>()
130
+                .eq(FeServicePlan::getStatus, STATUS_ACTIVE)
131
+                .ne(FeServicePlan::getPlanCode, PLAN_CURRENT)
132
+                .orderByAsc(FeServicePlan::getSortOrder)
133
+                .orderByAsc(FeServicePlan::getId));
134
+        List<FeServicePlan> effective = new ArrayList<>();
135
+        for (FeServicePlan plan : plans) {
136
+            if (plan.getEffectiveFrom() != null && today.isBefore(plan.getEffectiveFrom())) {
137
+                continue;
138
+            }
139
+            if (plan.getEffectiveTo() != null && today.isAfter(plan.getEffectiveTo())) {
140
+                continue;
141
+            }
142
+            effective.add(plan);
143
+        }
144
+        return effective;
145
+    }
146
+
147
+    private Map<Long, List<FeServicePlanFeeRule>> loadRulesByPlanIds(List<FeServicePlan> plans) {
148
+        List<Long> planIds = new ArrayList<>(plans.size());
149
+        for (FeServicePlan plan : plans) {
150
+            planIds.add(plan.getId());
151
+        }
152
+        List<FeServicePlanFeeRule> rules = feServicePlanFeeRuleMapper.selectList(
153
+                new LambdaQueryWrapper<FeServicePlanFeeRule>()
154
+                        .in(FeServicePlanFeeRule::getPlanId, planIds)
155
+                        .orderByAsc(FeServicePlanFeeRule::getSortOrder));
156
+        Map<Long, List<FeServicePlanFeeRule>> map = new HashMap<>();
157
+        for (FeServicePlanFeeRule rule : rules) {
158
+            map.computeIfAbsent(rule.getPlanId(), k -> new ArrayList<>()).add(rule);
159
+        }
160
+        return map;
161
+    }
162
+
163
+    private CalculatedScheme buildScheme(FeServicePlan plan,
164
+                                         List<FeServicePlanFeeRule> rules,
165
+                                         BigDecimal laborTotal,
166
+                                         int workerCount,
167
+                                         boolean recommended) {
53
         BigDecimal companyPay = scaleMoney(laborTotal);
168
         BigDecimal companyPay = scaleMoney(laborTotal);
54
-        BigDecimal serviceFee = scaleMoney(companyPay.multiply(SCHEME_A_SERVICE_FEE_RATE));
55
-        BigDecimal totalCost = companyPay.add(serviceFee);
56
         BigDecimal gross = companyPay;
169
         BigDecimal gross = companyPay;
170
+
171
+        FeServicePlanFeeRule serviceRule = findRule(rules, FEE_SERVICE);
172
+        BigDecimal serviceFee = null;
173
+        if (serviceRule != null) {
174
+            serviceFee = scaleMoney(calcFeeAmount(gross, serviceRule, plan));
175
+        } else if (plan.getServiceFeeRate() != null) {
176
+            serviceFee = scaleMoney(gross.multiply(plan.getServiceFeeRate()));
177
+        }
178
+
179
+        FeServicePlanFeeRule taxRule = findRule(rules, FEE_TAX);
57
         BigDecimal tax = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
180
         BigDecimal tax = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
58
-        BigDecimal net = gross;
181
+        if (taxRule != null) {
182
+            tax = scaleMoney(calcFeeAmount(gross, taxRule, plan));
183
+        }
184
+
185
+        BigDecimal net = scaleMoney(gross.subtract(tax));
186
+        BigDecimal totalCost = serviceFee != null ? companyPay.add(serviceFee) : companyPay;
187
+
59
         return new CalculatedScheme(
188
         return new CalculatedScheme(
60
-                SCHEME_A,
61
-                "灵活用工模式(推荐)",
62
-                Arrays.asList("合规", "低税负"),
63
-                true,
189
+                plan.getId(),
190
+                plan.getPlanCode(),
191
+                plan.getPlanName(),
192
+                parseTags(plan.getComplianceTags()),
193
+                recommended,
64
                 companyPay, serviceFee, totalCost,
194
                 companyPay, serviceFee, totalCost,
65
                 gross, tax, net,
195
                 gross, tax, net,
66
                 totalCost, net, workerCount);
196
                 totalCost, net, workerCount);
67
     }
197
     }
68
 
198
 
69
-    private CalculatedScheme buildSchemeB(BigDecimal laborTotal, int workerCount) {
70
-        BigDecimal companyPay = scaleMoney(laborTotal);
71
-        BigDecimal totalCost = companyPay;
72
-        BigDecimal gross = companyPay;
73
-        BigDecimal tax = scaleMoney(gross.multiply(SCHEME_B_TAX_RATE));
74
-        BigDecimal net = gross.subtract(tax);
75
-        return new CalculatedScheme(
76
-                SCHEME_B,
77
-                "劳务报酬模式",
78
-                Arrays.asList("传统"),
79
-                false,
80
-                companyPay, null, totalCost,
81
-                gross, tax, net,
82
-                totalCost, net, workerCount);
199
+    private static FeServicePlanFeeRule findRule(List<FeServicePlanFeeRule> rules, String feeCode) {
200
+        for (FeServicePlanFeeRule rule : rules) {
201
+            if (feeCode.equalsIgnoreCase(rule.getFeeCode())) {
202
+                return rule;
203
+            }
204
+        }
205
+        return null;
206
+    }
207
+
208
+    private BigDecimal calcFeeAmount(BigDecimal base, FeServicePlanFeeRule rule, FeServicePlan plan) {
209
+        String calcType = rule.getCalcType() == null ? "" : rule.getCalcType().trim().toLowerCase();
210
+        if (CALC_RATE.equals(calcType)) {
211
+            BigDecimal rate = rule.getDefaultRate();
212
+            if (rate == null && plan.getServiceFeeRate() != null
213
+                    && FEE_SERVICE.equalsIgnoreCase(rule.getFeeCode())) {
214
+                rate = plan.getServiceFeeRate();
215
+            }
216
+            if (rate == null) {
217
+                rate = parsePlainRate(rule.getCalcExpression());
218
+            }
219
+            if (rate == null) {
220
+                return BigDecimal.ZERO;
221
+            }
222
+            return base.multiply(rate);
223
+        }
224
+        if (CALC_FIXED.equals(calcType)) {
225
+            return rule.getDefaultRate() != null ? rule.getDefaultRate() : BigDecimal.ZERO;
226
+        }
227
+        if (CALC_FORMULA.equals(calcType)) {
228
+            return evalFormula(base, rule.getCalcExpression(), plan);
229
+        }
230
+        return BigDecimal.ZERO;
231
+    }
232
+
233
+    private static BigDecimal evalFormula(BigDecimal base, String expression, FeServicePlan plan) {
234
+        if (!StringUtils.hasText(expression)) {
235
+            return BigDecimal.ZERO;
236
+        }
237
+        String expr = expression.trim().replace(" ", "");
238
+        if ("gross".equalsIgnoreCase(expr) || "labor".equalsIgnoreCase(expr)
239
+                || "company_pay".equalsIgnoreCase(expr)) {
240
+            return base;
241
+        }
242
+        if (expr.toLowerCase().contains("service_fee_rate") && plan.getServiceFeeRate() != null) {
243
+            return base.multiply(plan.getServiceFeeRate());
244
+        }
245
+        Matcher mulDiv = FORMULA_MUL_DIV.matcher(expr);
246
+        if (mulDiv.matches()) {
247
+            return base.multiply(new BigDecimal(mulDiv.group(2)))
248
+                    .divide(new BigDecimal(mulDiv.group(3)), 8, RoundingMode.HALF_UP);
249
+        }
250
+        Matcher mul = FORMULA_MUL.matcher(expr);
251
+        if (mul.matches()) {
252
+            return base.multiply(new BigDecimal(mul.group(2)));
253
+        }
254
+        BigDecimal plain = parsePlainRate(expression);
255
+        return plain != null ? base.multiply(plain) : BigDecimal.ZERO;
256
+    }
257
+
258
+    private static BigDecimal parsePlainRate(String text) {
259
+        if (!StringUtils.hasText(text)) {
260
+            return null;
261
+        }
262
+        try {
263
+            return new BigDecimal(text.trim());
264
+        } catch (NumberFormatException ex) {
265
+            return null;
266
+        }
267
+    }
268
+
269
+    private List<String> parseTags(String complianceTagsJson) {
270
+        if (!StringUtils.hasText(complianceTagsJson)) {
271
+            return Collections.emptyList();
272
+        }
273
+        try {
274
+            List<String> tags = objectMapper.readValue(complianceTagsJson, new TypeReference<List<String>>() {
275
+            });
276
+            return tags != null ? tags : Collections.emptyList();
277
+        } catch (Exception ex) {
278
+            return Collections.emptyList();
279
+        }
83
     }
280
     }
84
 
281
 
85
     private McpDraftSchemeResponse toSchemeResponse(CalculatedScheme scheme) {
282
     private McpDraftSchemeResponse toSchemeResponse(CalculatedScheme scheme) {
@@ -117,6 +314,7 @@ public class DraftCostCalculationEngine {
117
      */
314
      */
118
     public static final class CalculatedScheme {
315
     public static final class CalculatedScheme {
119
 
316
 
317
+        private final Long planId;
120
         private final String code;
318
         private final String code;
121
         private final String title;
319
         private final String title;
122
         private final List<String> tags;
320
         private final List<String> tags;
@@ -131,7 +329,8 @@ public class DraftCostCalculationEngine {
131
         private final BigDecimal workerTakeHomePerWorker;
329
         private final BigDecimal workerTakeHomePerWorker;
132
         private final int workerCount;
330
         private final int workerCount;
133
 
331
 
134
-        public CalculatedScheme(String code,
332
+        public CalculatedScheme(Long planId,
333
+                                String code,
135
                                 String title,
334
                                 String title,
136
                                 List<String> tags,
335
                                 List<String> tags,
137
                                 boolean recommended,
336
                                 boolean recommended,
@@ -144,6 +343,7 @@ public class DraftCostCalculationEngine {
144
                                 BigDecimal employerTotalOutflow,
343
                                 BigDecimal employerTotalOutflow,
145
                                 BigDecimal workerTakeHomePerWorker,
344
                                 BigDecimal workerTakeHomePerWorker,
146
                                 int workerCount) {
345
                                 int workerCount) {
346
+            this.planId = planId;
147
             this.code = code;
347
             this.code = code;
148
             this.title = title;
348
             this.title = title;
149
             this.tags = tags;
349
             this.tags = tags;
@@ -161,6 +361,10 @@ public class DraftCostCalculationEngine {
161
             this.workerCount = workerCount;
361
             this.workerCount = workerCount;
162
         }
362
         }
163
 
363
 
364
+        public Long getPlanId() {
365
+            return planId;
366
+        }
367
+
164
         public String getCode() {
368
         public String getCode() {
165
             return code;
369
             return code;
166
         }
370
         }

+ 5 - 7
huimv-employment/fe-service/src/main/java/com/huimv/employment/service/draft/DraftService.java

@@ -57,9 +57,6 @@ public class DraftService {
57
             new HashSet<>(Arrays.asList("daily", "piece", "monthly")));
57
             new HashSet<>(Arrays.asList("daily", "piece", "monthly")));
58
     private static final Set<String> ALLOWED_INSURANCE_LEVELS = Collections.unmodifiableSet(
58
     private static final Set<String> ALLOWED_INSURANCE_LEVELS = Collections.unmodifiableSet(
59
             new HashSet<>(Arrays.asList("basic", "standard", "premium")));
59
             new HashSet<>(Arrays.asList("basic", "standard", "premium")));
60
-    private static final Set<String> ALLOWED_SCHEME_CODES = Collections.unmodifiableSet(
61
-            new HashSet<>(Arrays.asList(DraftCostCalculationEngine.SCHEME_A, DraftCostCalculationEngine.SCHEME_B)));
62
-
63
     private static final Map<String, String> FIELD_LABELS;
60
     private static final Map<String, String> FIELD_LABELS;
64
 
61
 
65
     static {
62
     static {
@@ -145,7 +142,7 @@ public class DraftService {
145
         }
142
         }
146
         FeEmploymentDraft draft = requireOwnedReady(userId, draftId);
143
         FeEmploymentDraft draft = requireOwnedReady(userId, draftId);
147
         String schemeCode = request.getSelectedSchemeCode().trim().toUpperCase();
144
         String schemeCode = request.getSelectedSchemeCode().trim().toUpperCase();
148
-        if (!ALLOWED_SCHEME_CODES.contains(schemeCode)) {
145
+        if (!draftCostCalculationEngine.isAllowedSchemeCode(schemeCode)) {
149
             throw new BizException(ErrorCode.BAD_REQUEST, "不支持的方案编码:" + request.getSelectedSchemeCode());
146
             throw new BizException(ErrorCode.BAD_REQUEST, "不支持的方案编码:" + request.getSelectedSchemeCode());
150
         }
147
         }
151
 
148
 
@@ -179,7 +176,7 @@ public class DraftService {
179
                 .set(FeEmploymentDraft::getStatus, STATUS_CONFIRMED)
176
                 .set(FeEmploymentDraft::getStatus, STATUS_CONFIRMED)
180
                 .set(FeEmploymentDraft::getConfirmedAt, now)
177
                 .set(FeEmploymentDraft::getConfirmedAt, now)
181
                 .set(FeEmploymentDraft::getConfirmedBy, userId)
178
                 .set(FeEmploymentDraft::getConfirmedBy, userId)
182
-                .set(FeEmploymentDraft::getSelectedPlanId, snapshot.getId())
179
+                .set(FeEmploymentDraft::getSelectedPlanId, snapshot.getPlanId())
183
                 .set(FeEmploymentDraft::getEstimatedTotal, snapshot.getEmployerTotalOutflow())
180
                 .set(FeEmploymentDraft::getEstimatedTotal, snapshot.getEmployerTotalOutflow())
184
                 .set(FeEmploymentDraft::getEstimatedPerCapita, snapshot.getEmployerAvgCostPerWorker())
181
                 .set(FeEmploymentDraft::getEstimatedPerCapita, snapshot.getEmployerAvgCostPerWorker())
185
                 .set(FeEmploymentDraft::getUpdateTime, now));
182
                 .set(FeEmploymentDraft::getUpdateTime, now));
@@ -215,6 +212,7 @@ public class DraftService {
215
         for (DraftCostCalculationEngine.CalculatedScheme scheme : schemes) {
212
         for (DraftCostCalculationEngine.CalculatedScheme scheme : schemes) {
216
             FeDraftCostSnapshot snapshot = new FeDraftCostSnapshot();
213
             FeDraftCostSnapshot snapshot = new FeDraftCostSnapshot();
217
             snapshot.setDraftId(draft.getId());
214
             snapshot.setDraftId(draft.getId());
215
+            snapshot.setPlanId(scheme.getPlanId());
218
             snapshot.setPlanCode(scheme.getCode());
216
             snapshot.setPlanCode(scheme.getCode());
219
             snapshot.setPlanName(scheme.getTitle());
217
             snapshot.setPlanName(scheme.getTitle());
220
             snapshot.setEmployerTotalOutflow(scheme.getEmployerTotalOutflow());
218
             snapshot.setEmployerTotalOutflow(scheme.getEmployerTotalOutflow());
@@ -235,9 +233,9 @@ public class DraftService {
235
                 .filter(DraftCostCalculationEngine.CalculatedScheme::isRecommended)
233
                 .filter(DraftCostCalculationEngine.CalculatedScheme::isRecommended)
236
                 .findFirst()
234
                 .findFirst()
237
                 .orElse(schemes.get(0));
235
                 .orElse(schemes.get(0));
236
+        // 仅刷新人均预估;保留 estimated_total 作为劳务费基数(total_amount),避免二次测算被污染
238
         FeEmploymentDraft update = new FeEmploymentDraft();
237
         FeEmploymentDraft update = new FeEmploymentDraft();
239
         update.setId(draft.getId());
238
         update.setId(draft.getId());
240
-        update.setEstimatedTotal(recommended.getEmployerTotalOutflow());
241
         update.setEstimatedPerCapita(recommended.getEmployerAvgCostPerWorker());
239
         update.setEstimatedPerCapita(recommended.getEmployerAvgCostPerWorker());
242
         update.setUpdateTime(now);
240
         update.setUpdateTime(now);
243
         feEmploymentDraftMapper.updateById(update);
241
         feEmploymentDraftMapper.updateById(update);
@@ -259,7 +257,7 @@ public class DraftService {
259
         order.setEnterpriseId(draft.getEnterpriseId());
257
         order.setEnterpriseId(draft.getEnterpriseId());
260
         order.setUserId(draft.getUserId() != null ? draft.getUserId() : userId);
258
         order.setUserId(draft.getUserId() != null ? draft.getUserId() : userId);
261
         order.setDraftId(draft.getId());
259
         order.setDraftId(draft.getId());
262
-        order.setSelectedPlanId(snapshot.getId());
260
+        order.setSelectedPlanId(snapshot.getPlanId());
263
         order.setTitle(StringUtils.hasText(draft.getTitle()) ? draft.getTitle() : "用工订单");
261
         order.setTitle(StringUtils.hasText(draft.getTitle()) ? draft.getTitle() : "用工订单");
264
         order.setWorkerCount(draft.getWorkerCount());
262
         order.setWorkerCount(draft.getWorkerCount());
265
         order.setWorkDays(draft.getWorkDays());
263
         order.setWorkDays(draft.getWorkDays());

+ 5 - 5
huimv-employment/fe-service/src/main/java/com/huimv/employment/service/draft/McpDraftService.java

@@ -74,9 +74,6 @@ public class McpDraftService {
74
     private static final Set<String> ALLOWED_SETTLEMENT_MODES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
74
     private static final Set<String> ALLOWED_SETTLEMENT_MODES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
75
             "daily", "piece", "monthly")));
75
             "daily", "piece", "monthly")));
76
 
76
 
77
-    private static final Set<String> ALLOWED_SCHEME_CODES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
78
-            DraftCostCalculationEngine.SCHEME_A, DraftCostCalculationEngine.SCHEME_B)));
79
-
80
     private final FeEmploymentDraftMapper feEmploymentDraftMapper;
77
     private final FeEmploymentDraftMapper feEmploymentDraftMapper;
81
     private final FeDraftFieldMapper feDraftFieldMapper;
78
     private final FeDraftFieldMapper feDraftFieldMapper;
82
     private final FeDraftCostSnapshotMapper feDraftCostSnapshotMapper;
79
     private final FeDraftCostSnapshotMapper feDraftCostSnapshotMapper;
@@ -176,7 +173,7 @@ public class McpDraftService {
176
         FeConversation conversation = conversationService.requireBySessionId(sessionId);
173
         FeConversation conversation = conversationService.requireBySessionId(sessionId);
177
         FeEmploymentDraft draft = requireReadyDraft(conversation.getId());
174
         FeEmploymentDraft draft = requireReadyDraft(conversation.getId());
178
         String schemeCode = request.getSelectedSchemeCode().trim().toUpperCase();
175
         String schemeCode = request.getSelectedSchemeCode().trim().toUpperCase();
179
-        if (!ALLOWED_SCHEME_CODES.contains(schemeCode)) {
176
+        if (!draftCostCalculationEngine.isAllowedSchemeCode(schemeCode)) {
180
             throw new BizException(ErrorCode.BAD_REQUEST, "不支持的方案编码:" + request.getSelectedSchemeCode());
177
             throw new BizException(ErrorCode.BAD_REQUEST, "不支持的方案编码:" + request.getSelectedSchemeCode());
181
         }
178
         }
182
         FeDraftCostSnapshot snapshot = findLatestSnapshot(draft.getId(), schemeCode);
179
         FeDraftCostSnapshot snapshot = findLatestSnapshot(draft.getId(), schemeCode);
@@ -199,6 +196,7 @@ public class McpDraftService {
199
         draftUpdate.setId(draft.getId());
196
         draftUpdate.setId(draft.getId());
200
         draftUpdate.setStatus(DB_STATUS_CONFIRMED);
197
         draftUpdate.setStatus(DB_STATUS_CONFIRMED);
201
         draftUpdate.setConfirmedAt(now);
198
         draftUpdate.setConfirmedAt(now);
199
+        draftUpdate.setSelectedPlanId(snapshot.getPlanId());
202
         draftUpdate.setEstimatedTotal(snapshot.getEmployerTotalOutflow());
200
         draftUpdate.setEstimatedTotal(snapshot.getEmployerTotalOutflow());
203
         draftUpdate.setEstimatedPerCapita(snapshot.getEmployerAvgCostPerWorker());
201
         draftUpdate.setEstimatedPerCapita(snapshot.getEmployerAvgCostPerWorker());
204
         draftUpdate.setUpdateTime(now);
202
         draftUpdate.setUpdateTime(now);
@@ -228,6 +226,7 @@ public class McpDraftService {
228
         for (DraftCostCalculationEngine.CalculatedScheme scheme : schemes) {
226
         for (DraftCostCalculationEngine.CalculatedScheme scheme : schemes) {
229
             FeDraftCostSnapshot snapshot = new FeDraftCostSnapshot();
227
             FeDraftCostSnapshot snapshot = new FeDraftCostSnapshot();
230
             snapshot.setDraftId(draft.getId());
228
             snapshot.setDraftId(draft.getId());
229
+            snapshot.setPlanId(scheme.getPlanId());
231
             snapshot.setPlanCode(scheme.getCode());
230
             snapshot.setPlanCode(scheme.getCode());
232
             snapshot.setPlanName(scheme.getTitle());
231
             snapshot.setPlanName(scheme.getTitle());
233
             snapshot.setEmployerTotalOutflow(scheme.getEmployerTotalOutflow());
232
             snapshot.setEmployerTotalOutflow(scheme.getEmployerTotalOutflow());
@@ -248,9 +247,9 @@ public class McpDraftService {
248
                 .filter(DraftCostCalculationEngine.CalculatedScheme::isRecommended)
247
                 .filter(DraftCostCalculationEngine.CalculatedScheme::isRecommended)
249
                 .findFirst()
248
                 .findFirst()
250
                 .orElse(schemes.get(0));
249
                 .orElse(schemes.get(0));
250
+        // 仅刷新人均预估;保留 estimated_total 作为劳务费基数(total_amount),避免二次测算被污染
251
         FeEmploymentDraft update = new FeEmploymentDraft();
251
         FeEmploymentDraft update = new FeEmploymentDraft();
252
         update.setId(draft.getId());
252
         update.setId(draft.getId());
253
-        update.setEstimatedTotal(recommended.getEmployerTotalOutflow());
254
         update.setEstimatedPerCapita(recommended.getEmployerAvgCostPerWorker());
253
         update.setEstimatedPerCapita(recommended.getEmployerAvgCostPerWorker());
255
         update.setUpdateTime(now);
254
         update.setUpdateTime(now);
256
         feEmploymentDraftMapper.updateById(update);
255
         feEmploymentDraftMapper.updateById(update);
@@ -271,6 +270,7 @@ public class McpDraftService {
271
         order.setEnterpriseId(draft.getEnterpriseId());
270
         order.setEnterpriseId(draft.getEnterpriseId());
272
         order.setUserId(draft.getUserId());
271
         order.setUserId(draft.getUserId());
273
         order.setDraftId(draft.getId());
272
         order.setDraftId(draft.getId());
273
+        order.setSelectedPlanId(snapshot.getPlanId());
274
         order.setTitle(StringUtils.hasText(draft.getTitle()) ? draft.getTitle() : "用工订单");
274
         order.setTitle(StringUtils.hasText(draft.getTitle()) ? draft.getTitle() : "用工订单");
275
         order.setWorkerCount(draft.getWorkerCount());
275
         order.setWorkerCount(draft.getWorkerCount());
276
         order.setWorkDays(draft.getWorkDays());
276
         order.setWorkDays(draft.getWorkDays());

+ 25 - 0
huimv-employment/fe-service/src/main/java/com/huimv/employment/service/draft/dto/DraftConfirmRequest.java

@@ -0,0 +1,25 @@
1
+package com.huimv.employment.service.draft.dto;
2
+
3
+import com.fasterxml.jackson.annotation.JsonAlias;
4
+import com.fasterxml.jackson.annotation.JsonProperty;
5
+import io.swagger.v3.oas.annotations.media.Schema;
6
+
7
+import javax.validation.constraints.NotBlank;
8
+
9
+@Schema(description = "小程序确认用工方案请求")
10
+public class DraftConfirmRequest {
11
+
12
+    @NotBlank(message = "selected_scheme_code 不能为空")
13
+    @JsonProperty("selected_scheme_code")
14
+    @JsonAlias({"selected_plan_id", "selectedPlanId", "selectedSchemeCode"})
15
+    @Schema(description = "选定的方案编码", example = "SCHEME_A", requiredMode = Schema.RequiredMode.REQUIRED)
16
+    private String selectedSchemeCode;
17
+
18
+    public String getSelectedSchemeCode() {
19
+        return selectedSchemeCode;
20
+    }
21
+
22
+    public void setSelectedSchemeCode(String selectedSchemeCode) {
23
+        this.selectedSchemeCode = selectedSchemeCode;
24
+    }
25
+}

+ 67 - 0
huimv-employment/fe-service/src/main/java/com/huimv/employment/service/draft/dto/DraftConfirmResponse.java

@@ -0,0 +1,67 @@
1
+package com.huimv.employment.service.draft.dto;
2
+
3
+import com.fasterxml.jackson.annotation.JsonProperty;
4
+import io.swagger.v3.oas.annotations.media.Schema;
5
+
6
+@Schema(description = "小程序确认用工方案响应")
7
+public class DraftConfirmResponse {
8
+
9
+    @Schema(description = "接口处理结果", example = "success")
10
+    private String status;
11
+
12
+    @JsonProperty("draft_status")
13
+    @Schema(description = "草稿状态,确认成功后为 confirmed", example = "confirmed")
14
+    private String draftStatus;
15
+
16
+    @JsonProperty("order_id")
17
+    @Schema(description = "生成的订单编号", example = "ord_123")
18
+    private String orderId;
19
+
20
+    @JsonProperty("draft_id")
21
+    @Schema(description = "草稿 ID")
22
+    private Long draftId;
23
+
24
+    @JsonProperty("selected_scheme_code")
25
+    @Schema(description = "确认的方案编码", example = "SCHEME_A")
26
+    private String selectedSchemeCode;
27
+
28
+    public String getStatus() {
29
+        return status;
30
+    }
31
+
32
+    public void setStatus(String status) {
33
+        this.status = status;
34
+    }
35
+
36
+    public String getDraftStatus() {
37
+        return draftStatus;
38
+    }
39
+
40
+    public void setDraftStatus(String draftStatus) {
41
+        this.draftStatus = draftStatus;
42
+    }
43
+
44
+    public String getOrderId() {
45
+        return orderId;
46
+    }
47
+
48
+    public void setOrderId(String orderId) {
49
+        this.orderId = orderId;
50
+    }
51
+
52
+    public Long getDraftId() {
53
+        return draftId;
54
+    }
55
+
56
+    public void setDraftId(Long draftId) {
57
+        this.draftId = draftId;
58
+    }
59
+
60
+    public String getSelectedSchemeCode() {
61
+        return selectedSchemeCode;
62
+    }
63
+
64
+    public void setSelectedSchemeCode(String selectedSchemeCode) {
65
+        this.selectedSchemeCode = selectedSchemeCode;
66
+    }
67
+}