hejianhao
2026-08-27 33bda988601fc15ef47a4cfc887f64f11229fb49
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package com.tcm.training.admin.system.service;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tcm.training.admin.system.dto.AgreementContentSaveRequest;
import com.tcm.training.admin.system.entity.AgreementContent;
import com.tcm.training.admin.system.mapper.AgreementContentMapper;
import com.tcm.training.admin.system.vo.AgreementContentItemVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.util.ArrayList;
import java.util.List;
 
@Service
public class AgreementContentAppService {
 
    private static final String[] AGREEMENT_TYPES = new String[]{
            AgreementContent.TYPE_USER_AGREEMENT,
            AgreementContent.TYPE_PRIVACY_POLICY,
            AgreementContent.TYPE_INVITE_CODE_INSTRUCTIONS
    };
 
    private final AgreementContentMapper agreementContentMapper;
 
    public AgreementContentAppService(AgreementContentMapper agreementContentMapper) {
        this.agreementContentMapper = agreementContentMapper;
    }
 
    public List<AgreementContentItemVO> list() {
        List<AgreementContent> records = agreementContentMapper.selectList(new LambdaQueryWrapper<AgreementContent>()
                .eq(AgreementContent::getDeleted, 0));
        List<AgreementContentItemVO> result = new ArrayList<AgreementContentItemVO>();
        for (String agreementType : AGREEMENT_TYPES) {
            result.add(toItemVO(agreementType, findByType(records, agreementType)));
        }
        return result;
    }
 
    /**
     * 按协议类型读取单条协议内容,供 APP 协议详情页使用。
     */
    public AgreementContentItemVO detail(String agreementType) {
        String normalizedType = normalizeAgreementType(agreementType);
        AgreementContent record = agreementContentMapper.selectOne(new LambdaQueryWrapper<AgreementContent>()
                .eq(AgreementContent::getAgreementType, normalizedType)
                .eq(AgreementContent::getDeleted, 0)
                .last("limit 1"));
        return toItemVO(normalizedType, record);
    }
 
    @Transactional
    public AgreementContentItemVO save(String agreementType, AgreementContentSaveRequest request) {
        String normalizedType = normalizeAgreementType(agreementType);
        String contentHtml = normalizeContent(request == null ? null : request.getContentHtml());
        AgreementContent record = agreementContentMapper.selectOne(new LambdaQueryWrapper<AgreementContent>()
                .eq(AgreementContent::getAgreementType, normalizedType)
                .eq(AgreementContent::getDeleted, 0)
                .last("limit 1"));
        if (record == null) {
            record = new AgreementContent();
            record.setAgreementType(normalizedType);
            record.setContentHtml(contentHtml);
            record.setDeleted(0);
            agreementContentMapper.insert(record);
        } else {
            record.setContentHtml(contentHtml);
            agreementContentMapper.updateById(record);
        }
        return toItemVO(normalizedType, record);
    }
 
    private AgreementContent findByType(List<AgreementContent> records, String agreementType) {
        for (AgreementContent record : records) {
            if (agreementType.equals(record.getAgreementType())) {
                return record;
            }
        }
        return null;
    }
 
    private AgreementContentItemVO toItemVO(String agreementType, AgreementContent record) {
        AgreementContentItemVO vo = new AgreementContentItemVO();
        vo.setAgreementType(agreementType);
        vo.setAgreementName(resolveAgreementName(agreementType));
        vo.setContentHtml(record == null || record.getContentHtml() == null ? "" : record.getContentHtml());
        return vo;
    }
 
    private String normalizeAgreementType(String agreementType) {
        String normalized = trimToNull(agreementType);
        if (normalized == null) {
            throw new IllegalArgumentException("协议类型不合法");
        }
        normalized = normalized.toUpperCase();
        for (String item : AGREEMENT_TYPES) {
            if (item.equals(normalized)) {
                return normalized;
            }
        }
        throw new IllegalArgumentException("协议类型不合法");
    }
 
    private String resolveAgreementName(String agreementType) {
        if (AgreementContent.TYPE_USER_AGREEMENT.equals(agreementType)) {
            return "用户协议";
        }
        if (AgreementContent.TYPE_PRIVACY_POLICY.equals(agreementType)) {
            return "隐私协议";
        }
        if (AgreementContent.TYPE_INVITE_CODE_INSTRUCTIONS.equals(agreementType)) {
            return "获取邀请码说明";
        }
        throw new IllegalArgumentException("协议类型不合法");
    }
 
    private String normalizeContent(String contentHtml) {
        String trimmed = trimToNull(contentHtml);
        if (trimmed == null || isBlankRichText(trimmed)) {
            throw new IllegalArgumentException("请输入协议内容");
        }
        return trimmed;
    }
 
    private boolean isBlankRichText(String html) {
        String normalized = html
                .replaceAll("(?i)<br\\s*/?>", "")
                .replaceAll("(?i)</p>", "")
                .replace("&nbsp;", "")
                .replace("&#160;", "")
                .replaceAll("<[^>]+>", "")
                .trim();
        if (!normalized.isEmpty()) {
            return false;
        }
        String lowerCaseHtml = html.toLowerCase();
        return !lowerCaseHtml.contains("<img")
                && !lowerCaseHtml.contains("<video")
                && !lowerCaseHtml.contains("<audio")
                && !lowerCaseHtml.contains("<iframe");
    }
 
    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }
}