spark-server/deploy/popadmin/price-package-patch/PricesConfigService.java
DESKTOP-L8OUL9F\gfl0034 5d05eb0804 chore(deploy): geo snippet、popadmin 补丁与订阅包名脚本
补充 world geo nginx snippet、OpenAPI/geo 热更新脚本,以及 popadmin package_name 定价补丁,便于运维对齐。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-30 09:17:18 +08:00

97 lines
4.1 KiB
Java

package com.wyrs.admin.service.config;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wyrs.admin.config.WyrsConfigProperties;
import com.wyrs.admin.entity.MembershipPrice;
import com.wyrs.admin.util.PackageNameContext;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import org.springframework.stereotype.Service;
@Service
public class PricesConfigService extends AbstractEntityListConfigService<MembershipPrice> {
private static final TypeReference<EntityListStore<MembershipPrice>> TYPE =
new TypeReference<EntityListStore<MembershipPrice>>() {};
public PricesConfigService(WyrsConfigProperties config, ObjectMapper objectMapper) {
super(config.resolveFile("prices.json"), objectMapper, TYPE, MembershipPrice::getId, MembershipPrice::getAppCode);
}
public List<MembershipPrice> findByAppCode(String appCode) {
// Admin: all packages under appCode
return this.findByAppCodeOrderByPriceAsc(appCode, false, null);
}
public List<MembershipPrice> findByAppCodeAndEnabledTrueOrderByPriceAsc(String appCode) {
// Client: filter by X-Wyrs-Package / packageName
return this.findByAppCodeOrderByPriceAsc(appCode, true, PackageNameContext.get());
}
private List<MembershipPrice> findByAppCodeOrderByPriceAsc(String appCode, boolean enabledOnly, String packageName) {
Object object = this.lock;
synchronized (object) {
List<MembershipPrice> base = this.store.getItems().stream()
.filter(p -> appCode.equals(p.getAppCode()))
.filter(p -> !enabledOnly || Boolean.TRUE.equals(p.getEnabled()))
.sorted(Comparator.comparing(MembershipPrice::getPrice, Comparator.nullsLast(BigDecimal::compareTo)))
.toList();
return filterByPackage(base, packageName);
}
}
/**
* Empty packageName: return all.
* Non-empty: prefer exact package match; else fall back to blank packageName defaults.
*/
public static List<MembershipPrice> filterByPackage(List<MembershipPrice> items, String packageName) {
if (packageName == null || packageName.isBlank()) {
return items;
}
String want = packageName.trim().toLowerCase(Locale.ROOT);
List<MembershipPrice> exact = items.stream()
.filter(p -> {
String pkg = p.getPackageName();
return pkg != null && !pkg.isBlank() && pkg.trim().toLowerCase(Locale.ROOT).equals(want);
})
.toList();
if (!exact.isEmpty()) {
return exact;
}
return items.stream()
.filter(p -> {
String pkg = p.getPackageName();
return pkg == null || pkg.isBlank();
})
.toList();
}
public MembershipPrice save(MembershipPrice price) {
if (price != null) {
if (price.getPackageName() == null) {
price.setPackageName("");
}
if (price.getBillingType() == null || price.getBillingType().isBlank()) {
Integer days = price.getDurationDays();
price.setBillingType(days != null && days > 0 ? "subscription" : "one_time");
} else {
String bt = price.getBillingType().trim().toLowerCase(Locale.ROOT);
if ("sub".equals(bt) || "renew".equals(bt) || "auto".equals(bt)) {
bt = "subscription";
}
if ("once".equals(bt) || "onetime".equals(bt) || "iap".equals(bt)) {
bt = "one_time";
}
price.setBillingType("subscription".equals(bt) ? "subscription" : "one_time");
}
if (price.getChannelProductCodes() == null) {
price.setChannelProductCodes(new LinkedHashMap<>());
}
}
return this.save(price, MembershipPrice::setId);
}
}