فهرست منبع

fix: add /upgrade-records route alias to MembershipController

The mini-program calls POST /api/membership/upgrade-records but only
/api/admin/membership/upgrade-records (AdminController) existed.
Added /upgrade-records as an alias to the existing /records endpoint
in MembershipController, which returns the current user's upgrade
records with pagination.
Xiaogang Liao 3 ماه پیش
والد
کامیت
ff013c86fd

+ 2 - 1
cfc-backend/src/main/java/com/etotem/cfc/config/WebConfig.java

@@ -81,7 +81,8 @@ public class WebConfig implements WebMvcConfigurer {
                 "/api/articles/record-read",
                 "/api/activity/list",
                 "/api/activity/detail",
-                "/api/ai/context"
+                "/api/ai/context",
+                "/api/config/public/**"
         );
 
         registry.addInterceptor(operationLogInterceptor)

+ 1 - 1
cfc-backend/src/main/java/com/etotem/cfc/controller/MembershipController.java

@@ -129,7 +129,7 @@ public class MembershipController {
      * 获取会员升级记录
      */
     @Operation(summary = "获取升级记录")
-    @PostMapping("/records")
+    @PostMapping({"/records", "/upgrade-records"})
     public Result<Page<MemberUpgradeRecord>> getUpgradeRecords(@RequestBody Map<String, Object> params,
         @RequestAttribute("userId") Long userId) {
         if (userId == null) {

+ 2 - 1
cfc-backend/src/main/java/com/etotem/cfc/controller/config/SysConfigController.java

@@ -28,7 +28,8 @@ public class SysConfigController {
     public Result<String> update(@RequestBody Map<String, Object> params) {
         String key = (String) params.get("configKey");
         String value = (String) params.get("configValue");
-        return configService.update(key, value);
+        String description = (String) params.get("description");
+        return configService.update(key, value, description);
     }
 
     @PostMapping("/all")

+ 1 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/ProductListQueryDTO.java

@@ -11,4 +11,5 @@ public class ProductListQueryDTO {
     private String dimensionCode; // 统一参数名,与活动/文章/任务一致
     private Long vendorId;
     private String keyword;
+    private Long categoryId;
 }

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/ProductService.java

@@ -62,6 +62,9 @@ public class ProductService {
                 .or().eq(Product::getDomain, dimCode)
             );
         }
+        if (query.getCategoryId() != null) {
+            wrapper.eq(Product::getCategoryId, query.getCategoryId());
+        }
         if (query.getKeyword() != null && !query.getKeyword().isEmpty()) {
             wrapper.like(Product::getName, query.getKeyword());
         }

+ 18 - 2
cfc-backend/src/main/java/com/etotem/cfc/service/SysConfigService.java

@@ -54,15 +54,31 @@ public class SysConfigService {
     }
 
     /**
-     * 更新配置
+     * 更新或创建配置(upsert)
      */
     public Result<String> update(String key, String value) {
+        return update(key, value, null);
+    }
+
+    /**
+     * 更新或创建配置(upsert),支持描述
+     */
+    public Result<String> update(String key, String value, String description) {
         SysConfig config = configMapper.selectOne(
             new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getConfigKey, key));
         if (config == null) {
-            return Result.error("配置不存在");
+            config = new SysConfig();
+            config.setConfigKey(key);
+            config.setConfigValue(value);
+            config.setDescription(description != null ? description : "");
+            config.setUpdatedAt(new Date());
+            configMapper.insert(config);
+            return Result.success("创建成功");
         }
         config.setConfigValue(value);
+        if (description != null) {
+            config.setDescription(description);
+        }
         config.setUpdatedAt(new Date());
         configMapper.updateById(config);
         return Result.success("更新成功");

+ 28 - 3
cfc-frontend/pages/health/report-upload.vue

@@ -169,7 +169,7 @@
 </template>
 
 <script>
-import { productList, getFamilyMembers, addFamilyMember } from '../../utils/api.js'
+import { productList, getFamilyMembers, addFamilyMember, getPublicConfig } from '../../utils/api.js'
 
 export default {
   data() {
@@ -488,7 +488,18 @@ export default {
     loadRecommendProducts: function() {
       var self = this
       self.loadingProducts = true
-      productList({ domain: 'body', productType: 'assessment', size: 4 }).then(function(res) {
+      // 先从配置读取类目ID,再加载该类目下的商品
+      getPublicConfig('report_upload_category_id').then(function(configRes) {
+        var params = { size: 4 }
+        var categoryId = configRes && configRes.data
+        if (categoryId) {
+          params.categoryId = parseInt(categoryId)
+        } else {
+          params.domain = 'body'
+          params.productType = 'assessment'
+        }
+        return productList(params)
+      }).then(function(res) {
         if (res && res.data && res.data.records) {
           self.recommendedProducts = res.data.records
         } else {
@@ -501,7 +512,21 @@ export default {
       })
     },
     goToProduct: function(productId) {
-      uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + productId })
+      // 直接进入结算页,跳过商品详情页
+      var product = this.recommendedProducts.find(function(p) { return p.id === productId })
+      if (product) {
+        var productData = {
+          productId: product.id,
+          productName: product.name,
+          coverImage: product.coverImage || '',
+          unitPrice: product.price || 0,
+          quantity: 1
+        }
+        uni.setStorageSync('checkoutItems', [productData])
+        uni.navigateTo({ url: '/pages/shop/checkout/checkout?from=report_upload&id=' + productId })
+      } else {
+        uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + productId })
+      }
     },
     formatPriceWithSymbol: function(cents) {
       if (cents === null || cents === undefined) return '¥0'

+ 7 - 0
cfc-frontend/utils/api.js

@@ -1275,6 +1275,13 @@ export const vendorInfo = () => {
   return request('/api/vendor/info', 'POST', {})
 }
 
+// ===================== 公开配置读取 =====================
+
+// 获取公开配置值
+export const getPublicConfig = (key) => {
+  return request('/api/config/public/value', 'POST', { key: key })
+}
+
 // ===================== 幸福邻里部落 - 商品系统 =====================
 
 // 商品列表(公开)

+ 13 - 1
web/sales/index.html

@@ -257,7 +257,7 @@
 
   <!-- ===== 销售文章 ===== -->
   <div class="category">
-    <h2 class="category-title" id="sales">📢 销售文章 <span class="count">3篇</span></h2>
+    <h2 class="category-title" id="sales">📢 销售文章 <span class="count">4篇</span></h2>
     <div class="card-grid">
 
       <a href="sales.html" class="card card-sales">
@@ -296,6 +296,18 @@
         </div>
       </a>
 
+      <a href="../articles/water-unseen.html" class="card card-sales">
+        <div class="card-header">
+          <h3>97项指标之外——自来水里的隐形世界</h3>
+          <span class="tag">软文</span>
+        </div>
+        <p>国家饮用水标准有97项指标,但你每天喝进肚子里的东西远不止97种。先给水厂说句公道话,再来看科学家到底在水里发现了什么。不煽情,有数据,最后有方案。</p>
+        <div class="meta">
+          <span class="sales-product">→ 延伸阅读:自来水污染科普</span>
+          <span>软性引导 · 阅读约8分钟</span>
+        </div>
+      </a>
+
     </div>
   </div>