Explorar o código

1.完成用户管理接口开发
2.完成框架ip地址调整
3.完成数据库脚本更新
4.完成字典数据更新

steelwei hai 1 mes
pai
achega
85bbd2b2a9
Modificáronse 49 ficheiros con 2645 adicións e 21 borrados
  1. 5 3
      pom.xml
  2. 1 1
      ruoyi-auth/src/main/resources/application.yml
  3. 1 1
      ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java
  4. 2 2
      ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml
  5. 1 1
      ruoyi-gateway/src/main/resources/application.yml
  6. 1 1
      ruoyi-modules/ruoyi-gen/src/main/resources/application.yml
  7. 1 1
      ruoyi-modules/ruoyi-job/src/main/resources/application.yml
  8. 1 1
      ruoyi-modules/ruoyi-resource/src/main/resources/application.yml
  9. 1 1
      ruoyi-modules/ruoyi-system/src/main/resources/application.yml
  10. 1 1
      ruoyi-modules/ruoyi-workflow/src/main/resources/application.yml
  11. 53 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/controller/ArcChangeLogController.java
  12. 111 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/controller/SysTagController.java
  13. 121 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/controller/UsrCustomerController.java
  14. 113 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/controller/UsrPetController.java
  15. 61 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/ArcChangeLog.java
  16. 66 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/SysTag.java
  17. 43 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/SysTagRel.java
  18. 146 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/UsrCustomer.java
  19. 82 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/UsrPet.java
  20. 61 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/bo/SysTagBo.java
  21. 118 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/bo/UsrCustomerBo.java
  22. 92 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/bo/UsrPetBo.java
  23. 37 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/vo/ArcChangeLogVo.java
  24. 75 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/vo/SysTagVo.java
  25. 95 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/vo/UsrCustomerVo.java
  26. 114 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/vo/UsrPetVo.java
  27. 14 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/ArcChangeLogMapper.java
  28. 15 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/SysTagMapper.java
  29. 14 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/SysTagRelMapper.java
  30. 15 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/UsrCustomerMapper.java
  31. 15 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/UsrPetMapper.java
  32. 32 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/IArcChangeLogService.java
  33. 49 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/ISysTagService.java
  34. 54 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/IUsrCustomerService.java
  35. 49 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/IUsrPetService.java
  36. 86 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/impl/ArcChangeLogServiceImpl.java
  37. 92 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/impl/SysTagServiceImpl.java
  38. 176 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/impl/UsrCustomerServiceImpl.java
  39. 224 0
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/impl/UsrPetServiceImpl.java
  40. 1 1
      ruoyi-modules/yingpaipay-archieves/src/main/resources/application.yml
  41. 1 1
      ruoyi-modules/yingpaipay-fulfiller/src/main/resources/application.yml
  42. 1 1
      ruoyi-modules/yingpaipay-service/src/main/resources/application.yml
  43. 1 1
      ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties
  44. 9 4
      script/config/nacos/datasource.yml
  45. 21 0
      script/config/nacos/ruoyi-gateway.yml
  46. 124 0
      script/sql/business/archive.sql
  47. 178 0
      script/sql/business/fulfiller.sql
  48. 70 0
      script/sql/business/update.sql
  49. 1 0
      script/sql/ry-cloud.sql

+ 5 - 3
pom.xml

@@ -68,13 +68,14 @@
             <id>dev</id>
             <properties>
                 <!-- 环境标识,需要与配置文件的名称相对应 -->
-                <profiles.active>dev</profiles.active>
-                <nacos.server>192.168.1.118:8848</nacos.server>
+                <profiles.active>test</profiles.active>
+                <nacos.server>127.0.0.1:8848</nacos.server>
                 <nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group>
                 <nacos.config.group>DEFAULT_GROUP</nacos.config.group>
                 <nacos.username>nacos</nacos.username>
                 <nacos.password>nacos</nacos.password>
-                <logstash.address>192.168.1.118:4560</logstash.address>
+                <logstash.address>127.0.0.1:4560</logstash.address>
+                <discovery.ip>192.168.1.140</discovery.ip>
             </properties>
             <activation>
                 <!-- 默认环境 -->
@@ -91,6 +92,7 @@
                 <nacos.username>nacos</nacos.username>
                 <nacos.password>nacos</nacos.password>
                 <logstash.address>127.0.0.1:4560</logstash.address>
+                <discovery.ip>192.168.1.140</discovery.ip>
             </properties>
         </profile>
     </profiles>

+ 1 - 1
ruoyi-auth/src/main/resources/application.yml

@@ -20,7 +20,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-common/ruoyi-common-dubbo/src/main/java/org/apache/dubbo/metadata/store/redis/RedisMetadataReport.java

@@ -64,7 +64,7 @@ public class RedisMetadataReport extends AbstractMetadataReport {
     public RedisMetadataReport(URL url) {
         super(url);
         timeout = url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
-        username = url.getUsername();
+        username = (StringUtils.isBlank(url.getUsername()) || "default".equals(url.getUsername())) ? null : url.getUsername();
         password = url.getPassword();
         this.root = url.getGroup(DEFAULT_ROOT);
         if (url.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) {

+ 2 - 2
ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml

@@ -22,8 +22,8 @@ dubbo:
   metadata-report:
     address: redis://${spring.data.redis.host:localhost}:${spring.data.redis.port:6379}
     group: DUBBO_GROUP
-    username: ${spring.data.redis.username:ithuanyi}
-    password: ${spring.data.redis.password:123456}
+    username: default
+    password: ${spring.data.redis.password:ruoyi123}
     parameters:
       namespace: ${spring.profiles.active}
       database: ${spring.data.redis.database}

+ 1 - 1
ruoyi-gateway/src/main/resources/application.yml

@@ -22,7 +22,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-modules/ruoyi-gen/src/main/resources/application.yml

@@ -20,7 +20,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-modules/ruoyi-job/src/main/resources/application.yml

@@ -20,7 +20,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-modules/ruoyi-resource/src/main/resources/application.yml

@@ -24,7 +24,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/resources/application.yml

@@ -20,7 +20,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-modules/ruoyi-workflow/src/main/resources/application.yml

@@ -20,7 +20,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 53 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/controller/ArcChangeLogController.java

@@ -0,0 +1,53 @@
+package org.dromara.archieves.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.archieves.domain.vo.ArcChangeLogVo;
+import org.dromara.archieves.service.IArcChangeLogService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 档案变更日志
+ * 前端访问路由地址为:/archieves/changeLog
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/changeLog")
+public class ArcChangeLogController extends BaseController {
+
+    private final IArcChangeLogService arcChangeLogService;
+
+    /**
+     * 分页查询变更日志
+     */
+    @SaCheckPermission("archieves:changeLog:list")
+    @GetMapping("/list")
+    public TableDataInfo<ArcChangeLogVo> list(
+        @RequestParam Long targetId,
+        @RequestParam String targetType,
+        PageQuery pageQuery) {
+        return arcChangeLogService.queryPageByTarget(targetId, targetType, pageQuery);
+    }
+
+    /**
+     * 查询全部变更日志(不分页)
+     */
+    @GetMapping("/listAll")
+    public R<List<ArcChangeLogVo>> listAll(
+        @RequestParam Long targetId,
+        @RequestParam String targetType) {
+        return R.ok(arcChangeLogService.queryListByTarget(targetId, targetType));
+    }
+
+}

+ 111 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/controller/SysTagController.java

@@ -0,0 +1,111 @@
+package org.dromara.archieves.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.archieves.domain.vo.SysTagVo;
+import org.dromara.archieves.domain.bo.SysTagBo;
+import org.dromara.archieves.service.ISysTagService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 标签管理
+ * 前端访问路由地址为:/archieves/tag
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/tag")
+public class SysTagController extends BaseController {
+
+    private final ISysTagService sysTagService;
+
+    /**
+     * 查询标签列表
+     */
+    @SaCheckPermission("archieves:tag:list")
+    @GetMapping("/list")
+    public TableDataInfo<SysTagVo> list(SysTagBo bo, PageQuery pageQuery) {
+        return sysTagService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 查询标签列表(不分页,供选择器使用)
+     */
+    @GetMapping("/listAll")
+    public R<List<SysTagVo>> listAll(SysTagBo bo) {
+        return R.ok(sysTagService.queryList(bo));
+    }
+
+    /**
+     * 导出标签列表
+     */
+    @SaCheckPermission("archieves:tag:export")
+    @Log(title = "标签管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(SysTagBo bo, HttpServletResponse response) {
+        List<SysTagVo> list = sysTagService.queryList(bo);
+        ExcelUtil.exportExcel(list, "标签管理", SysTagVo.class, response);
+    }
+
+    /**
+     * 获取标签详细信息
+     */
+    @SaCheckPermission("archieves:tag:query")
+    @GetMapping("/{id}")
+    public R<SysTagVo> getInfo(@NotNull(message = "主键不能为空")
+                               @PathVariable("id") Long id) {
+        return R.ok(sysTagService.queryById(id));
+    }
+
+    /**
+     * 新增标签
+     */
+    @SaCheckPermission("archieves:tag:add")
+    @Log(title = "标签管理", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody SysTagBo bo) {
+        return toAjax(sysTagService.insertByBo(bo));
+    }
+
+    /**
+     * 修改标签
+     */
+    @SaCheckPermission("archieves:tag:edit")
+    @Log(title = "标签管理", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysTagBo bo) {
+        return toAjax(sysTagService.updateByBo(bo));
+    }
+
+    /**
+     * 删除标签
+     */
+    @SaCheckPermission("archieves:tag:remove")
+    @Log(title = "标签管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(sysTagService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+}

+ 121 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/controller/UsrCustomerController.java

@@ -0,0 +1,121 @@
+package org.dromara.archieves.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.archieves.domain.vo.UsrCustomerVo;
+import org.dromara.archieves.domain.bo.UsrCustomerBo;
+import org.dromara.archieves.service.IUsrCustomerService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 用户管理
+ * 前端访问路由地址为:/archieves/customer
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/customer")
+public class UsrCustomerController extends BaseController {
+
+    private final IUsrCustomerService usrCustomerService;
+
+    /**
+     * 查询用户列表
+     */
+    @SaCheckPermission("archieves:customer:list")
+    @GetMapping("/list")
+    public TableDataInfo<UsrCustomerVo> list(UsrCustomerBo bo, PageQuery pageQuery) {
+        return usrCustomerService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 查询用户列表(不分页,供选择器使用)
+     */
+    @GetMapping("/listAll")
+    public R<List<UsrCustomerVo>> listAll(UsrCustomerBo bo) {
+        return R.ok(usrCustomerService.queryList(bo));
+    }
+
+    /**
+     * 导出用户列表
+     */
+    @SaCheckPermission("archieves:customer:export")
+    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(UsrCustomerBo bo, HttpServletResponse response) {
+        List<UsrCustomerVo> list = usrCustomerService.queryList(bo);
+        ExcelUtil.exportExcel(list, "用户管理", UsrCustomerVo.class, response);
+    }
+
+    /**
+     * 获取用户详细信息
+     */
+    @SaCheckPermission("archieves:customer:query")
+    @GetMapping("/{id}")
+    public R<UsrCustomerVo> getInfo(@NotNull(message = "主键不能为空")
+                                    @PathVariable("id") Long id) {
+        return R.ok(usrCustomerService.queryById(id));
+    }
+
+    /**
+     * 新增用户
+     */
+    @SaCheckPermission("archieves:customer:add")
+    @Log(title = "用户管理", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody UsrCustomerBo bo) {
+        return toAjax(usrCustomerService.insertByBo(bo));
+    }
+
+    /**
+     * 修改用户
+     */
+    @SaCheckPermission("archieves:customer:edit")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody UsrCustomerBo bo) {
+        return toAjax(usrCustomerService.updateByBo(bo));
+    }
+
+    /**
+     * 删除用户
+     */
+    @SaCheckPermission("archieves:customer:remove")
+    @Log(title = "用户管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(usrCustomerService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    /**
+     * 切换用户状态
+     */
+    @SaCheckPermission("archieves:customer:edit")
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public R<Void> changeStatus(@RequestParam Long id, @RequestParam Integer status) {
+        return toAjax(usrCustomerService.changeStatus(id, status));
+    }
+
+}

+ 113 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/controller/UsrPetController.java

@@ -0,0 +1,113 @@
+package org.dromara.archieves.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.archieves.domain.vo.UsrPetVo;
+import org.dromara.archieves.domain.bo.UsrPetBo;
+import org.dromara.archieves.service.IUsrPetService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 宠物档案
+ * 前端访问路由地址为:/archieves/pet
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/pet")
+public class UsrPetController extends BaseController {
+
+    private final IUsrPetService usrPetService;
+
+    /**
+     * 查询宠物列表
+     */
+    @SaCheckPermission("archieves:pet:list")
+    @GetMapping("/list")
+    public TableDataInfo<UsrPetVo> list(UsrPetBo bo, PageQuery pageQuery) {
+        return usrPetService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 按用户查询宠物列表
+     */
+    @SaCheckPermission("archieves:pet:list")
+    @GetMapping("/listByUser/{userId}")
+    public R<List<UsrPetVo>> listByUser(@NotNull(message = "用户ID不能为空")
+                                        @PathVariable("userId") Long userId) {
+        return R.ok(usrPetService.queryListByUserId(userId));
+    }
+
+    /**
+     * 导出宠物列表
+     */
+    @SaCheckPermission("archieves:pet:export")
+    @Log(title = "宠物档案", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(UsrPetBo bo, HttpServletResponse response) {
+        List<UsrPetVo> list = usrPetService.queryListByUserId(null);
+        ExcelUtil.exportExcel(list, "宠物档案", UsrPetVo.class, response);
+    }
+
+    /**
+     * 获取宠物详细信息
+     */
+    @SaCheckPermission("archieves:pet:query")
+    @GetMapping("/{id}")
+    public R<UsrPetVo> getInfo(@NotNull(message = "主键不能为空")
+                               @PathVariable("id") Long id) {
+        return R.ok(usrPetService.queryById(id));
+    }
+
+    /**
+     * 新增宠物
+     */
+    @SaCheckPermission("archieves:pet:add")
+    @Log(title = "宠物档案", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody UsrPetBo bo) {
+        return toAjax(usrPetService.insertByBo(bo));
+    }
+
+    /**
+     * 修改宠物
+     */
+    @SaCheckPermission("archieves:pet:edit")
+    @Log(title = "宠物档案", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody UsrPetBo bo) {
+        return toAjax(usrPetService.updateByBo(bo));
+    }
+
+    /**
+     * 删除宠物
+     */
+    @SaCheckPermission("archieves:pet:remove")
+    @Log(title = "宠物档案", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空")
+                          @PathVariable("ids") Long[] ids) {
+        return toAjax(usrPetService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+}

+ 61 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/ArcChangeLog.java

@@ -0,0 +1,61 @@
+package org.dromara.archieves.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 档案变更日志对象 arc_change_log
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@TableName("arc_change_log")
+public class ArcChangeLog implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 目标对象ID
+     */
+    private Long targetId;
+
+    /**
+     * 目标类型 (user, pet)
+     */
+    private String targetType;
+
+    /**
+     * 日志类型 (remark, system, change)
+     */
+    private String logType;
+
+    /**
+     * 变更内容
+     */
+    private String content;
+
+    /**
+     * 操作人ID
+     */
+    private Long operatorId;
+
+    /**
+     * 操作人姓名
+     */
+    private String operatorName;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+}

+ 66 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/SysTag.java

@@ -0,0 +1,66 @@
+package org.dromara.archieves.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 标签定义对象 sys_tag
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_tag")
+public class SysTag extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 标签名称
+     */
+    private String name;
+
+    /**
+     * 分类 (user, pet, fulfiller)
+     */
+    private String category;
+
+    /**
+     * 颜色样式 (primary, success, warning, danger, info)
+     */
+    private String colorType;
+
+    /**
+     * 标签说明
+     */
+    private String description;
+
+    /**
+     * 类型 (1:系统内置, 2:自定义)
+     */
+    private Integer type;
+
+    /**
+     * 状态 (0:启用, 1:停用)
+     */
+    private Integer status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+}

+ 43 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/SysTagRel.java

@@ -0,0 +1,43 @@
+package org.dromara.archieves.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 标签关联对象 sys_tag_rel
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@TableName("sys_tag_rel")
+public class SysTagRel implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 标签ID
+     */
+    private Long tagId;
+
+    /**
+     * 目标对象ID
+     */
+    private Long targetId;
+
+    /**
+     * 目标类型 (user, pet, fulfiller)
+     */
+    private String targetType;
+
+}

+ 146 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/UsrCustomer.java

@@ -0,0 +1,146 @@
+package org.dromara.archieves.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * C端用户对象 usr_customer
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("usr_customer")
+public class UsrCustomer extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 微信OpenID
+     */
+    private String openId;
+
+    /**
+     * 昵称
+     */
+    private String nickname;
+
+    /**
+     * 真实姓名
+     */
+    private String name;
+
+    /**
+     * 性别 (0:男, 1:女, 2:未知)
+     */
+    private Integer gender;
+
+    /**
+     * 手机号
+     */
+    private String phone;
+
+    /**
+     * 头像
+     */
+    private Long avatar;
+
+    /**
+     * 常驻区域ID
+     */
+    private Long areaId;
+
+    /**
+     * 所属站点ID
+     */
+    private Long stationId;
+
+    /**
+     * 省市区编码
+     */
+    private String regionCode;
+
+    /**
+     * 详细住址
+     */
+    private String address;
+
+    /**
+     * 房屋类型 (stairs:楼梯, elevator:电梯)
+     */
+    private String houseType;
+
+    /**
+     * 入门方式 (password:密码, key:钥匙)
+     */
+    private String entryMethod;
+
+    /**
+     * 开门密码
+     */
+    private String entryPassword;
+
+    /**
+     * 钥匙位置
+     */
+    private String keyLocation;
+
+    /**
+     * 来源渠道
+     */
+    private String source;
+
+    /**
+     * 宠物数量
+     */
+    private Integer petCount;
+
+    /**
+     * 积分
+     */
+    private Integer points;
+
+    /**
+     * 余额(分)
+     */
+    private Long balance;
+
+    /**
+     * 下单次数
+     */
+    private Integer orderCount;
+
+    /**
+     * 总消费额(分)
+     */
+    private Long totalConsume;
+
+    /**
+     * 状态 (0:正常, 1:冻结)
+     */
+    private Integer status;
+
+    /**
+     * 后台备注
+     */
+    private String remark;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+}

+ 82 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/UsrPet.java

@@ -0,0 +1,82 @@
+package org.dromara.archieves.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 宠物档案对象 usr_pet
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("usr_pet")
+public class UsrPet extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id")
+    private Long id;
+
+    private Long userId;
+
+    private String name;
+
+    private Long avatar;
+
+    private Integer type;
+
+    private String breed;
+
+    private Integer gender;
+
+    private Date birthday;
+
+    private Integer age;
+
+    private BigDecimal weight;
+
+    private String size;
+
+    private Integer isSterilized;
+
+    private Date arrivalTime;
+
+    private String houseType;
+
+    private String entryMethod;
+
+    private String entryPassword;
+
+    private String keyLocation;
+
+    private String personality;
+
+    private String cutePersonality;
+
+    private String healthStatus;
+
+    private Boolean aggression;
+
+    private String vaccineStatus;
+
+    private Long vaccineCert;
+
+    private String medicalHistory;
+
+    private String allergies;
+
+    private String remark;
+
+    @TableLogic
+    private String delFlag;
+
+}

+ 61 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/bo/SysTagBo.java

@@ -0,0 +1,61 @@
+package org.dromara.archieves.domain.bo;
+
+import org.dromara.archieves.domain.SysTag;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 标签定义业务对象 sys_tag
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = SysTag.class, reverseConvertGenerate = false)
+public class SysTagBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 标签名称
+     */
+    @NotBlank(message = "标签名称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String name;
+
+    /**
+     * 分类 (user, pet, fulfiller)
+     */
+    @NotBlank(message = "分类不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String category;
+
+    /**
+     * 颜色样式
+     */
+    private String colorType;
+
+    /**
+     * 标签说明
+     */
+    private String description;
+
+    /**
+     * 类型 (1:系统内置, 2:自定义)
+     */
+    private Integer type;
+
+    /**
+     * 状态 (0:启用, 1:停用)
+     */
+    private Integer status;
+
+}

+ 118 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/bo/UsrCustomerBo.java

@@ -0,0 +1,118 @@
+package org.dromara.archieves.domain.bo;
+
+import org.dromara.archieves.domain.UsrCustomer;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import java.util.List;
+
+/**
+ * C端用户业务对象 usr_customer
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = UsrCustomer.class, reverseConvertGenerate = false)
+public class UsrCustomerBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 真实姓名
+     */
+    @NotBlank(message = "姓名不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String name;
+
+    /**
+     * 手机号
+     */
+    @NotBlank(message = "手机号不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String phone;
+
+    /**
+     * 性别
+     */
+    private Integer gender;
+
+    /**
+     * 头像
+     */
+    private Long avatar;
+
+    /**
+     * 常驻区域ID
+     */
+    private Long areaId;
+
+    /**
+     * 所属站点ID
+     */
+    private Long stationId;
+
+    /**
+     * 省市区编码
+     */
+    private String regionCode;
+
+    /**
+     * 详细住址
+     */
+    private String address;
+
+    /**
+     * 房屋类型
+     */
+    private String houseType;
+
+    /**
+     * 入门方式
+     */
+    private String entryMethod;
+
+    /**
+     * 开门密码
+     */
+    private String entryPassword;
+
+    /**
+     * 钥匙位置
+     */
+    private String keyLocation;
+
+    /**
+     * 来源渠道
+     */
+    private String source;
+
+    /**
+     * 状态
+     */
+    private Integer status;
+
+    /**
+     * 后台备注
+     */
+    private String remark;
+
+    /**
+     * 搜索关键字(姓名/手机号)
+     */
+    private String keyword;
+
+    /**
+     * 标签ID列表(新增/编辑时传入)
+     */
+    private List<Long> tagIds;
+
+}

+ 92 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/bo/UsrPetBo.java

@@ -0,0 +1,92 @@
+package org.dromara.archieves.domain.bo;
+
+import org.dromara.archieves.domain.UsrPet;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 宠物档案业务对象 usr_pet
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = UsrPet.class, reverseConvertGenerate = false)
+public class UsrPetBo extends BaseEntity {
+
+    @NotNull(message = "主键不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    @NotNull(message = "所属用户不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long userId;
+
+    @NotBlank(message = "宠物昵称不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String name;
+
+    private Long avatar;
+
+    private Integer type;
+
+    private String breed;
+
+    private Integer gender;
+
+    private Date birthday;
+
+    private Integer age;
+
+    private BigDecimal weight;
+
+    private String size;
+
+    private Integer isSterilized;
+
+    private Date arrivalTime;
+
+    private String houseType;
+
+    private String entryMethod;
+
+    private String entryPassword;
+
+    private String keyLocation;
+
+    private String personality;
+
+    private String cutePersonality;
+
+    private String healthStatus;
+
+    private Boolean aggression;
+
+    private String vaccineStatus;
+
+    private Long vaccineCert;
+
+    private String medicalHistory;
+
+    private String allergies;
+
+    private String remark;
+
+    /**
+     * 搜索关键字
+     */
+    private String keyword;
+
+    /**
+     * 标签ID列表
+     */
+    private List<Long> tagIds;
+
+}

+ 37 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/vo/ArcChangeLogVo.java

@@ -0,0 +1,37 @@
+package org.dromara.archieves.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 档案变更日志视图对象 arc_change_log
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+public class ArcChangeLogVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    private Long targetId;
+
+    private String targetType;
+
+    private String logType;
+
+    private String content;
+
+    private Long operatorId;
+
+    private String operatorName;
+
+    private Date createTime;
+
+}

+ 75 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/vo/SysTagVo.java

@@ -0,0 +1,75 @@
+package org.dromara.archieves.domain.vo;
+
+import org.dromara.archieves.domain.SysTag;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 标签定义视图对象 sys_tag
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SysTag.class)
+public class SysTagVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 标签名称
+     */
+    @ExcelProperty(value = "标签名称")
+    private String name;
+
+    /**
+     * 分类
+     */
+    @ExcelProperty(value = "分类")
+    private String category;
+
+    /**
+     * 颜色样式
+     */
+    @ExcelProperty(value = "颜色样式")
+    private String colorType;
+
+    /**
+     * 标签说明
+     */
+    @ExcelProperty(value = "标签说明")
+    private String description;
+
+    /**
+     * 类型
+     */
+    @ExcelProperty(value = "类型")
+    private Integer type;
+
+    /**
+     * 状态
+     */
+    @ExcelProperty(value = "状态")
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+}

+ 95 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/vo/UsrCustomerVo.java

@@ -0,0 +1,95 @@
+package org.dromara.archieves.domain.vo;
+
+import org.dromara.archieves.domain.UsrCustomer;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * C端用户视图对象 usr_customer
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = UsrCustomer.class)
+public class UsrCustomerVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    private String openId;
+
+    @ExcelProperty(value = "昵称")
+    private String nickname;
+
+    @ExcelProperty(value = "姓名")
+    private String name;
+
+    @ExcelProperty(value = "性别")
+    private Integer gender;
+
+    @ExcelProperty(value = "手机号")
+    private String phone;
+
+    private Long avatar;
+
+    @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "avatar")
+    private String avatarUrl;
+
+    private Long areaId;
+
+    private Long stationId;
+
+    private String regionCode;
+
+    @ExcelProperty(value = "住址")
+    private String address;
+
+    private String houseType;
+
+    private String entryMethod;
+
+    private String entryPassword;
+
+    private String keyLocation;
+
+    @ExcelProperty(value = "来源")
+    private String source;
+
+    private Integer petCount;
+
+    private Integer points;
+
+    private Long balance;
+
+    private Integer orderCount;
+
+    private Long totalConsume;
+
+    @ExcelProperty(value = "状态")
+    private Integer status;
+
+    private String remark;
+
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+    /**
+     * 关联标签列表
+     */
+    private List<SysTagVo> tags;
+
+}

+ 114 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/domain/vo/UsrPetVo.java

@@ -0,0 +1,114 @@
+package org.dromara.archieves.domain.vo;
+
+import org.dromara.archieves.domain.UsrPet;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 宠物档案视图对象 usr_pet
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = UsrPet.class)
+public class UsrPetVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    private Long userId;
+
+    @ExcelProperty(value = "宠物昵称")
+    private String name;
+
+    private Long avatar;
+
+    @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "avatar")
+    private String avatarUrl;
+
+    @ExcelProperty(value = "类型")
+    private Integer type;
+
+    @ExcelProperty(value = "品种")
+    private String breed;
+
+    @ExcelProperty(value = "性别")
+    private Integer gender;
+
+    private Date birthday;
+
+    @ExcelProperty(value = "年龄")
+    private Integer age;
+
+    private BigDecimal weight;
+
+    private String size;
+
+    private Integer isSterilized;
+
+    private Date arrivalTime;
+
+    private String houseType;
+
+    private String entryMethod;
+
+    private String entryPassword;
+
+    private String keyLocation;
+
+    private String personality;
+
+    private String cutePersonality;
+
+    @ExcelProperty(value = "健康状况")
+    private String healthStatus;
+
+    private Boolean aggression;
+
+    private String vaccineStatus;
+
+    private Long vaccineCert;
+
+    @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "vaccineCert")
+    private String vaccineCertUrl;
+
+    private String medicalHistory;
+
+    private String allergies;
+
+    private String remark;
+
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+    /**
+     * 主人姓名(关联查询)
+     */
+    private String ownerName;
+
+    /**
+     * 主人手机号(关联查询)
+     */
+    private String ownerPhone;
+
+    /**
+     * 关联标签列表
+     */
+    private List<SysTagVo> tags;
+
+}

+ 14 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/ArcChangeLogMapper.java

@@ -0,0 +1,14 @@
+package org.dromara.archieves.mapper;
+
+import org.dromara.archieves.domain.ArcChangeLog;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 档案变更日志Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface ArcChangeLogMapper extends BaseMapperPlus<ArcChangeLog, ArcChangeLog> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/SysTagMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.archieves.mapper;
+
+import org.dromara.archieves.domain.SysTag;
+import org.dromara.archieves.domain.vo.SysTagVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 标签定义Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface SysTagMapper extends BaseMapperPlus<SysTag, SysTagVo> {
+
+}

+ 14 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/SysTagRelMapper.java

@@ -0,0 +1,14 @@
+package org.dromara.archieves.mapper;
+
+import org.dromara.archieves.domain.SysTagRel;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 标签关联Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface SysTagRelMapper extends BaseMapperPlus<SysTagRel, SysTagRel> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/UsrCustomerMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.archieves.mapper;
+
+import org.dromara.archieves.domain.UsrCustomer;
+import org.dromara.archieves.domain.vo.UsrCustomerVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * C端用户Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface UsrCustomerMapper extends BaseMapperPlus<UsrCustomer, UsrCustomerVo> {
+
+}

+ 15 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/mapper/UsrPetMapper.java

@@ -0,0 +1,15 @@
+package org.dromara.archieves.mapper;
+
+import org.dromara.archieves.domain.UsrPet;
+import org.dromara.archieves.domain.vo.UsrPetVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 宠物档案Mapper接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface UsrPetMapper extends BaseMapperPlus<UsrPet, UsrPetVo> {
+
+}

+ 32 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/IArcChangeLogService.java

@@ -0,0 +1,32 @@
+package org.dromara.archieves.service;
+
+import org.dromara.archieves.domain.vo.ArcChangeLogVo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.List;
+
+/**
+ * 档案变更日志Service接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface IArcChangeLogService {
+
+    /**
+     * 按目标分页查询变更日志
+     */
+    TableDataInfo<ArcChangeLogVo> queryPageByTarget(Long targetId, String targetType, PageQuery pageQuery);
+
+    /**
+     * 按目标查询全部变更日志
+     */
+    List<ArcChangeLogVo> queryListByTarget(Long targetId, String targetType);
+
+    /**
+     * 新增变更日志
+     */
+    Boolean addLog(Long targetId, String targetType, String logType, String content);
+
+}

+ 49 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/ISysTagService.java

@@ -0,0 +1,49 @@
+package org.dromara.archieves.service;
+
+import org.dromara.archieves.domain.vo.SysTagVo;
+import org.dromara.archieves.domain.bo.SysTagBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 标签定义Service接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface ISysTagService {
+
+    /**
+     * 查询标签
+     */
+    SysTagVo queryById(Long id);
+
+    /**
+     * 分页查询标签列表
+     */
+    TableDataInfo<SysTagVo> queryPageList(SysTagBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询标签列表(不分页)
+     */
+    List<SysTagVo> queryList(SysTagBo bo);
+
+    /**
+     * 新增标签
+     */
+    Boolean insertByBo(SysTagBo bo);
+
+    /**
+     * 修改标签
+     */
+    Boolean updateByBo(SysTagBo bo);
+
+    /**
+     * 批量删除标签
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+}

+ 54 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/IUsrCustomerService.java

@@ -0,0 +1,54 @@
+package org.dromara.archieves.service;
+
+import org.dromara.archieves.domain.vo.UsrCustomerVo;
+import org.dromara.archieves.domain.bo.UsrCustomerBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * C端用户Service接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface IUsrCustomerService {
+
+    /**
+     * 查询用户
+     */
+    UsrCustomerVo queryById(Long id);
+
+    /**
+     * 分页查询用户列表
+     */
+    TableDataInfo<UsrCustomerVo> queryPageList(UsrCustomerBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询用户列表(不分页)
+     */
+    List<UsrCustomerVo> queryList(UsrCustomerBo bo);
+
+    /**
+     * 新增用户
+     */
+    Boolean insertByBo(UsrCustomerBo bo);
+
+    /**
+     * 修改用户
+     */
+    Boolean updateByBo(UsrCustomerBo bo);
+
+    /**
+     * 批量删除用户
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 切换用户状态
+     */
+    Boolean changeStatus(Long id, Integer status);
+
+}

+ 49 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/IUsrPetService.java

@@ -0,0 +1,49 @@
+package org.dromara.archieves.service;
+
+import org.dromara.archieves.domain.vo.UsrPetVo;
+import org.dromara.archieves.domain.bo.UsrPetBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 宠物档案Service接口
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+public interface IUsrPetService {
+
+    /**
+     * 查询宠物
+     */
+    UsrPetVo queryById(Long id);
+
+    /**
+     * 分页查询宠物列表
+     */
+    TableDataInfo<UsrPetVo> queryPageList(UsrPetBo bo, PageQuery pageQuery);
+
+    /**
+     * 按用户ID查询宠物列表
+     */
+    List<UsrPetVo> queryListByUserId(Long userId);
+
+    /**
+     * 新增宠物
+     */
+    Boolean insertByBo(UsrPetBo bo);
+
+    /**
+     * 修改宠物
+     */
+    Boolean updateByBo(UsrPetBo bo);
+
+    /**
+     * 批量删除宠物
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+}

+ 86 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/impl/ArcChangeLogServiceImpl.java

@@ -0,0 +1,86 @@
+package org.dromara.archieves.service.impl;
+
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.satoken.utils.LoginHelper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.archieves.domain.ArcChangeLog;
+import org.dromara.archieves.domain.vo.ArcChangeLogVo;
+import org.dromara.archieves.mapper.ArcChangeLogMapper;
+import org.dromara.archieves.service.IArcChangeLogService;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 档案变更日志Service业务层处理
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class ArcChangeLogServiceImpl implements IArcChangeLogService {
+
+    private final ArcChangeLogMapper baseMapper;
+
+    @Override
+    public TableDataInfo<ArcChangeLogVo> queryPageByTarget(Long targetId, String targetType, PageQuery pageQuery) {
+        LambdaQueryWrapper<ArcChangeLog> lqw = Wrappers.lambdaQuery();
+        lqw.eq(ArcChangeLog::getTargetId, targetId);
+        lqw.eq(ArcChangeLog::getTargetType, targetType);
+        lqw.orderByDesc(ArcChangeLog::getCreateTime);
+        Page<ArcChangeLog> page = baseMapper.selectPage(pageQuery.build(), lqw);
+        // 手动转换为Vo
+        Page<ArcChangeLogVo> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
+        voPage.setRecords(page.getRecords().stream().map(this::toVo).collect(Collectors.toList()));
+        return TableDataInfo.build(voPage);
+    }
+
+    @Override
+    public List<ArcChangeLogVo> queryListByTarget(Long targetId, String targetType) {
+        LambdaQueryWrapper<ArcChangeLog> lqw = Wrappers.lambdaQuery();
+        lqw.eq(ArcChangeLog::getTargetId, targetId);
+        lqw.eq(ArcChangeLog::getTargetType, targetType);
+        lqw.orderByDesc(ArcChangeLog::getCreateTime);
+        return baseMapper.selectList(lqw).stream().map(this::toVo).collect(Collectors.toList());
+    }
+
+    @Override
+    public Boolean addLog(Long targetId, String targetType, String logType, String content) {
+        ArcChangeLog entity = new ArcChangeLog();
+        entity.setTargetId(targetId);
+        entity.setTargetType(targetType);
+        entity.setLogType(logType);
+        entity.setContent(content);
+        entity.setCreateTime(new Date());
+        try {
+            entity.setOperatorId(LoginHelper.getUserId());
+            entity.setOperatorName(LoginHelper.getUsername());
+        } catch (Exception e) {
+            log.warn("获取当前登录用户信息失败, 变更日志将不记录操作人");
+        }
+        return baseMapper.insert(entity) > 0;
+    }
+
+    private ArcChangeLogVo toVo(ArcChangeLog entity) {
+        ArcChangeLogVo vo = new ArcChangeLogVo();
+        vo.setId(entity.getId());
+        vo.setTargetId(entity.getTargetId());
+        vo.setTargetType(entity.getTargetType());
+        vo.setLogType(entity.getLogType());
+        vo.setContent(entity.getContent());
+        vo.setOperatorId(entity.getOperatorId());
+        vo.setOperatorName(entity.getOperatorName());
+        vo.setCreateTime(entity.getCreateTime());
+        return vo;
+    }
+
+}

+ 92 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/impl/SysTagServiceImpl.java

@@ -0,0 +1,92 @@
+package org.dromara.archieves.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.archieves.domain.bo.SysTagBo;
+import org.dromara.archieves.domain.vo.SysTagVo;
+import org.dromara.archieves.domain.SysTag;
+import org.dromara.archieves.mapper.SysTagMapper;
+import org.dromara.archieves.service.ISysTagService;
+
+import java.util.List;
+import java.util.Collection;
+
+/**
+ * 标签定义Service业务层处理
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class SysTagServiceImpl implements ISysTagService {
+
+    private final SysTagMapper baseMapper;
+
+    @Override
+    public SysTagVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    @Override
+    public TableDataInfo<SysTagVo> queryPageList(SysTagBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<SysTag> lqw = buildQueryWrapper(bo);
+        Page<SysTagVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    @Override
+    public List<SysTagVo> queryList(SysTagBo bo) {
+        LambdaQueryWrapper<SysTag> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<SysTag> buildQueryWrapper(SysTagBo bo) {
+        LambdaQueryWrapper<SysTag> lqw = Wrappers.lambdaQuery();
+        lqw.like(StringUtils.isNotBlank(bo.getName()), SysTag::getName, bo.getName());
+        lqw.eq(StringUtils.isNotBlank(bo.getCategory()), SysTag::getCategory, bo.getCategory());
+        lqw.eq(bo.getStatus() != null, SysTag::getStatus, bo.getStatus());
+        lqw.orderByDesc(SysTag::getCreateTime);
+        return lqw;
+    }
+
+    @Override
+    public Boolean insertByBo(SysTagBo bo) {
+        SysTag add = MapstructUtils.convert(bo, SysTag.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    @Override
+    public Boolean updateByBo(SysTagBo bo) {
+        SysTag update = MapstructUtils.convert(bo, SysTag.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    private void validEntityBeforeSave(SysTag entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+}

+ 176 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/impl/UsrCustomerServiceImpl.java

@@ -0,0 +1,176 @@
+package org.dromara.archieves.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.dromara.archieves.domain.SysTagRel;
+import org.dromara.archieves.domain.bo.UsrCustomerBo;
+import org.dromara.archieves.domain.vo.UsrCustomerVo;
+import org.dromara.archieves.domain.vo.SysTagVo;
+import org.dromara.archieves.domain.UsrCustomer;
+import org.dromara.archieves.mapper.UsrCustomerMapper;
+import org.dromara.archieves.mapper.SysTagRelMapper;
+import org.dromara.archieves.mapper.SysTagMapper;
+import org.dromara.archieves.service.IUsrCustomerService;
+
+import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+/**
+ * C端用户Service业务层处理
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class UsrCustomerServiceImpl implements IUsrCustomerService {
+
+    private final UsrCustomerMapper baseMapper;
+    private final SysTagRelMapper tagRelMapper;
+    private final SysTagMapper tagMapper;
+
+    @Override
+    public UsrCustomerVo queryById(Long id) {
+        UsrCustomerVo vo = baseMapper.selectVoById(id);
+        if (vo != null) {
+            vo.setTags(queryTagsByTargetId(id, "user"));
+        }
+        return vo;
+    }
+
+    @Override
+    public TableDataInfo<UsrCustomerVo> queryPageList(UsrCustomerBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<UsrCustomer> lqw = buildQueryWrapper(bo);
+        Page<UsrCustomerVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        // 填充标签
+        result.getRecords().forEach(vo -> vo.setTags(queryTagsByTargetId(vo.getId(), "user")));
+        return TableDataInfo.build(result);
+    }
+
+    @Override
+    public List<UsrCustomerVo> queryList(UsrCustomerBo bo) {
+        LambdaQueryWrapper<UsrCustomer> lqw = buildQueryWrapper(bo);
+        List<UsrCustomerVo> list = baseMapper.selectVoList(lqw);
+        list.forEach(vo -> vo.setTags(queryTagsByTargetId(vo.getId(), "user")));
+        return list;
+    }
+
+    private LambdaQueryWrapper<UsrCustomer> buildQueryWrapper(UsrCustomerBo bo) {
+        LambdaQueryWrapper<UsrCustomer> lqw = Wrappers.lambdaQuery();
+        lqw.and(StringUtils.isNotBlank(bo.getKeyword()), w ->
+            w.like(UsrCustomer::getName, bo.getKeyword())
+                .or().like(UsrCustomer::getPhone, bo.getKeyword())
+        );
+        lqw.eq(bo.getAreaId() != null, UsrCustomer::getAreaId, bo.getAreaId());
+        lqw.eq(bo.getStationId() != null, UsrCustomer::getStationId, bo.getStationId());
+        lqw.like(StringUtils.isNotBlank(bo.getRegionCode()), UsrCustomer::getRegionCode, bo.getRegionCode());
+        lqw.eq(bo.getStatus() != null, UsrCustomer::getStatus, bo.getStatus());
+        lqw.orderByDesc(UsrCustomer::getCreateTime);
+        return lqw;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean insertByBo(UsrCustomerBo bo) {
+        UsrCustomer add = MapstructUtils.convert(bo, UsrCustomer.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+            saveTagRels(add.getId(), bo.getTagIds(), "user");
+        }
+        return flag;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateByBo(UsrCustomerBo bo) {
+        UsrCustomer update = MapstructUtils.convert(bo, UsrCustomer.class);
+        validEntityBeforeSave(update);
+        boolean flag = baseMapper.updateById(update) > 0;
+        if (flag) {
+            saveTagRels(bo.getId(), bo.getTagIds(), "user");
+        }
+        return flag;
+    }
+
+    private void validEntityBeforeSave(UsrCustomer entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验
+        }
+        // 删除标签关联
+        ids.forEach(id -> tagRelMapper.delete(
+            Wrappers.lambdaQuery(SysTagRel.class)
+                .eq(SysTagRel::getTargetId, id)
+                .eq(SysTagRel::getTargetType, "user")
+        ));
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    @Override
+    public Boolean changeStatus(Long id, Integer status) {
+        UsrCustomer update = new UsrCustomer();
+        update.setId(id);
+        update.setStatus(status);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 查询目标关联的标签列表
+     */
+    private List<SysTagVo> queryTagsByTargetId(Long targetId, String targetType) {
+        List<SysTagRel> rels = tagRelMapper.selectList(
+            Wrappers.lambdaQuery(SysTagRel.class)
+                .eq(SysTagRel::getTargetId, targetId)
+                .eq(SysTagRel::getTargetType, targetType)
+        );
+        if (rels.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<Long> tagIds = rels.stream().map(SysTagRel::getTagId).collect(Collectors.toList());
+        return tagMapper.selectVoList(
+            Wrappers.lambdaQuery(org.dromara.archieves.domain.SysTag.class)
+                .in(org.dromara.archieves.domain.SysTag::getId, tagIds)
+        );
+    }
+
+    /**
+     * 保存标签关联(先删后增)
+     */
+    private void saveTagRels(Long targetId, List<Long> tagIds, String targetType) {
+        // 先删除旧关联
+        tagRelMapper.delete(
+            Wrappers.lambdaQuery(SysTagRel.class)
+                .eq(SysTagRel::getTargetId, targetId)
+                .eq(SysTagRel::getTargetType, targetType)
+        );
+        // 再新增
+        if (tagIds != null && !tagIds.isEmpty()) {
+            tagIds.forEach(tagId -> {
+                SysTagRel rel = new SysTagRel();
+                rel.setTagId(tagId);
+                rel.setTargetId(targetId);
+                rel.setTargetType(targetType);
+                tagRelMapper.insert(rel);
+            });
+        }
+    }
+
+}

+ 224 - 0
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/service/impl/UsrPetServiceImpl.java

@@ -0,0 +1,224 @@
+package org.dromara.archieves.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.dromara.archieves.domain.SysTag;
+import org.dromara.archieves.domain.SysTagRel;
+import org.dromara.archieves.domain.UsrCustomer;
+import org.dromara.archieves.domain.bo.UsrPetBo;
+import org.dromara.archieves.domain.vo.UsrPetVo;
+import org.dromara.archieves.domain.vo.SysTagVo;
+import org.dromara.archieves.domain.UsrPet;
+import org.dromara.archieves.mapper.UsrPetMapper;
+import org.dromara.archieves.mapper.UsrCustomerMapper;
+import org.dromara.archieves.mapper.SysTagRelMapper;
+import org.dromara.archieves.mapper.SysTagMapper;
+import org.dromara.archieves.service.IUsrPetService;
+
+import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+/**
+ * 宠物档案Service业务层处理
+ *
+ * @author steelwei
+ * @date 2026-02-27
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class UsrPetServiceImpl implements IUsrPetService {
+
+    private final UsrPetMapper baseMapper;
+    private final UsrCustomerMapper customerMapper;
+    private final SysTagRelMapper tagRelMapper;
+    private final SysTagMapper tagMapper;
+
+    @Override
+    public UsrPetVo queryById(Long id) {
+        UsrPetVo vo = baseMapper.selectVoById(id);
+        if (vo != null) {
+            fillOwnerInfo(vo);
+            vo.setTags(queryTagsByTargetId(id, "pet"));
+        }
+        return vo;
+    }
+
+    @Override
+    public TableDataInfo<UsrPetVo> queryPageList(UsrPetBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<UsrPet> lqw = buildQueryWrapper(bo);
+        Page<UsrPetVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        result.getRecords().forEach(vo -> {
+            fillOwnerInfo(vo);
+            vo.setTags(queryTagsByTargetId(vo.getId(), "pet"));
+        });
+        return TableDataInfo.build(result);
+    }
+
+    @Override
+    public List<UsrPetVo> queryListByUserId(Long userId) {
+        LambdaQueryWrapper<UsrPet> lqw = Wrappers.lambdaQuery();
+        lqw.eq(UsrPet::getUserId, userId);
+        lqw.orderByDesc(UsrPet::getCreateTime);
+        List<UsrPetVo> list = baseMapper.selectVoList(lqw);
+        list.forEach(vo -> {
+            fillOwnerInfo(vo);
+            vo.setTags(queryTagsByTargetId(vo.getId(), "pet"));
+        });
+        return list;
+    }
+
+    private LambdaQueryWrapper<UsrPet> buildQueryWrapper(UsrPetBo bo) {
+        LambdaQueryWrapper<UsrPet> lqw = Wrappers.lambdaQuery();
+        if (StringUtils.isNotBlank(bo.getKeyword())) {
+            // 先查匹配主人名/手机号的userId列表
+            List<Long> matchUserIds = customerMapper.selectList(
+                Wrappers.lambdaQuery(UsrCustomer.class)
+                    .like(UsrCustomer::getName, bo.getKeyword())
+                    .or()
+                    .like(UsrCustomer::getPhone, bo.getKeyword())
+            ).stream().map(UsrCustomer::getId).collect(Collectors.toList());
+            // 宠物名 OR 主人名/手机号
+            lqw.and(w -> {
+                w.like(UsrPet::getName, bo.getKeyword());
+                if (!matchUserIds.isEmpty()) {
+                    w.or().in(UsrPet::getUserId, matchUserIds);
+                }
+            });
+        }
+        lqw.eq(bo.getUserId() != null, UsrPet::getUserId, bo.getUserId());
+        lqw.orderByDesc(UsrPet::getCreateTime);
+        return lqw;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean insertByBo(UsrPetBo bo) {
+        UsrPet add = MapstructUtils.convert(bo, UsrPet.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+            saveTagRels(add.getId(), bo.getTagIds(), "pet");
+            // 更新用户宠物数量
+            updateCustomerPetCount(bo.getUserId());
+        }
+        return flag;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateByBo(UsrPetBo bo) {
+        UsrPet update = MapstructUtils.convert(bo, UsrPet.class);
+        validEntityBeforeSave(update);
+        boolean flag = baseMapper.updateById(update) > 0;
+        if (flag) {
+            saveTagRels(bo.getId(), bo.getTagIds(), "pet");
+        }
+        return flag;
+    }
+
+    private void validEntityBeforeSave(UsrPet entity) {
+        //TODO 做一些数据校验
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验
+        }
+        // 获取宠物对应的用户ID,用于后续更新宠物数量
+        List<UsrPet> pets = baseMapper.selectBatchIds(ids);
+        // 删除标签关联
+        ids.forEach(id -> tagRelMapper.delete(
+            Wrappers.lambdaQuery(SysTagRel.class)
+                .eq(SysTagRel::getTargetId, id)
+                .eq(SysTagRel::getTargetType, "pet")
+        ));
+        boolean flag = baseMapper.deleteByIds(ids) > 0;
+        if (flag) {
+            pets.stream().map(UsrPet::getUserId).distinct().forEach(this::updateCustomerPetCount);
+        }
+        return flag;
+    }
+
+    /**
+     * 填充主人信息
+     */
+    private void fillOwnerInfo(UsrPetVo vo) {
+        if (vo.getUserId() != null) {
+            UsrCustomer customer = customerMapper.selectById(vo.getUserId());
+            if (customer != null) {
+                vo.setOwnerName(customer.getName());
+                vo.setOwnerPhone(customer.getPhone());
+            }
+        }
+    }
+
+    /**
+     * 更新用户宠物数量
+     */
+    private void updateCustomerPetCount(Long userId) {
+        if (userId == null) {
+            return;
+        }
+        Long count = baseMapper.selectCount(
+            Wrappers.lambdaQuery(UsrPet.class).eq(UsrPet::getUserId, userId)
+        );
+        UsrCustomer update = new UsrCustomer();
+        update.setId(userId);
+        update.setPetCount(count.intValue());
+        customerMapper.updateById(update);
+    }
+
+    /**
+     * 查询目标关联的标签列表
+     */
+    private List<SysTagVo> queryTagsByTargetId(Long targetId, String targetType) {
+        List<SysTagRel> rels = tagRelMapper.selectList(
+            Wrappers.lambdaQuery(SysTagRel.class)
+                .eq(SysTagRel::getTargetId, targetId)
+                .eq(SysTagRel::getTargetType, targetType)
+        );
+        if (rels.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<Long> tagIds = rels.stream().map(SysTagRel::getTagId).collect(Collectors.toList());
+        return tagMapper.selectVoList(
+            Wrappers.lambdaQuery(SysTag.class).in(SysTag::getId, tagIds)
+        );
+    }
+
+    /**
+     * 保存标签关联(先删后增)
+     */
+    private void saveTagRels(Long targetId, List<Long> tagIds, String targetType) {
+        tagRelMapper.delete(
+            Wrappers.lambdaQuery(SysTagRel.class)
+                .eq(SysTagRel::getTargetId, targetId)
+                .eq(SysTagRel::getTargetType, targetType)
+        );
+        if (tagIds != null && !tagIds.isEmpty()) {
+            tagIds.forEach(tagId -> {
+                SysTagRel rel = new SysTagRel();
+                rel.setTagId(tagId);
+                rel.setTargetId(targetId);
+                rel.setTargetType(targetType);
+                tagRelMapper.insert(rel);
+            });
+        }
+    }
+
+}

+ 1 - 1
ruoyi-modules/yingpaipay-archieves/src/main/resources/application.yml

@@ -20,7 +20,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-modules/yingpaipay-fulfiller/src/main/resources/application.yml

@@ -20,7 +20,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-modules/yingpaipay-service/src/main/resources/application.yml

@@ -20,7 +20,7 @@ spring:
       username: @nacos.username@
       password: @nacos.password@
       discovery:
-        ip: 192.168.1.118
+        ip: @discovery.ip@
         # 注册组
         group: @nacos.discovery.group@
         namespace: ${spring.profiles.active}

+ 1 - 1
ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties

@@ -28,7 +28,7 @@ server.port=8848
 # nacos.inetutils.prefer-hostname-over-ip=false
 
 ### Specify local server's IP:
-nacos.inetutils.ip-address=192.168.1.118
+nacos.inetutils.ip-address=192.168.1.140
 
 spring.application.name=ruoyi-nacos
 #*************** Config Module Related Configurations ***************#

+ 9 - 4
script/config/nacos/datasource.yml

@@ -4,19 +4,24 @@ datasource:
     # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能
     url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
     username: root
-    password: password
+    password: root
   gen:
     url: jdbc:mysql://localhost:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
     username: root
-    password: password
+    password: root
   job:
     url: jdbc:mysql://localhost:3306/ry-job?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
     username: root
-    password: password
+    password: root
   workflow:
     url: jdbc:mysql://localhost:3306/ry-workflow?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
     username: root
-    password: password
+    password: root
+  # 宠宝业务库(服务管理/档案管理/履约者管理)
+  pet-system:
+    url: jdbc:mysql://localhost:3306/pet_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+    username: root
+    password: root
 #  system-oracle:
 #    url: jdbc:oracle:thin:@//localhost:1521/XE
 #    username: ROOT

+ 21 - 0
script/config/nacos/ruoyi-gateway.yml

@@ -69,6 +69,27 @@ spring:
               uri: lb://ruoyi-workflow
               predicates:
                 - Path=/warm-flow-ui/**,/warm-flow/**
+            # 服务管理
+            - id: yingpaipay-service
+              uri: lb://yingpaipay-service
+              predicates:
+                - Path=/service/**
+              filters:
+                - StripPrefix=1
+            # 档案管理
+            - id: yingpaipay-archieves
+              uri: lb://yingpaipay-archieves
+              predicates:
+                - Path=/archieves/**
+              filters:
+                - StripPrefix=1
+            # 履约者管理
+            - id: yingpaipay-fulfiller
+              uri: lb://yingpaipay-fulfiller
+              predicates:
+                - Path=/fulfiller/**
+              filters:
+                - StripPrefix=1
             # 演示服务
             - id: ruoyi-demo
               uri: lb://ruoyi-demo

+ 124 - 0
script/sql/business/archive.sql

@@ -0,0 +1,124 @@
+# 2026/02/27 档案管理模块
+
+# ============================
+# 用户档案表
+# ============================
+CREATE TABLE `pet_system`.`usr_customer`
+(
+    `id`             bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `open_id`        varchar(100) COMMENT '微信OpenID',
+    `nickname`       varchar(50) COMMENT '昵称',
+    `name`           varchar(50) COMMENT '真实姓名',
+    `gender`         tinyint    DEFAULT 0 COMMENT '性别 (0:男, 1:女, 2:未知)',
+    `phone`          varchar(20) COMMENT '手机号',
+    `avatar`         bigint COMMENT '头像',
+    `area_id`        bigint COMMENT '常驻区域ID',
+    `station_id`     bigint COMMENT '所属站点ID',
+    `region_code`    varchar(20) COMMENT '省市区编码',
+    `address`        varchar(255) COMMENT '详细住址',
+    `house_type`     varchar(20) DEFAULT 'elevator' COMMENT '房屋类型 (stairs:楼梯, elevator:电梯)',
+    `entry_method`   varchar(20) DEFAULT 'password' COMMENT '入门方式 (password:密码, key:钥匙)',
+    `entry_password` varchar(50) COMMENT '开门密码',
+    `key_location`   varchar(100) COMMENT '钥匙位置',
+    `source`         varchar(50) COMMENT '来源渠道',
+    `pet_count`      int        DEFAULT 0 COMMENT '宠物数量',
+    `points`         int        DEFAULT 0 COMMENT '积分',
+    `balance`        bigint     DEFAULT 0 COMMENT '余额(分)',
+    `order_count`    int        DEFAULT 0 COMMENT '下单次数',
+    `total_consume`  bigint     DEFAULT 0 COMMENT '总消费额(分)',
+    `status`         tinyint    DEFAULT 0 COMMENT '状态 (0:正常, 1:冻结)',
+    `remark`         varchar(500) COMMENT '后台备注',
+    `del_flag`       char(1)    DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
+    `create_dept`    bigint(20) COMMENT '创建部门',
+    `create_by`      bigint(20) COMMENT '创建者',
+    `create_time`    datetime COMMENT '创建时间',
+    `update_by`      bigint(20) COMMENT '更新者',
+    `update_time`    datetime COMMENT '更新时间'
+) ENGINE = innoDB COMMENT = 'C端用户表';
+
+# ============================
+# 宠物档案表
+# ============================
+CREATE TABLE `pet_system`.`usr_pet`
+(
+    `id`               bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `user_id`          bigint             NOT NULL COMMENT '所属用户ID',
+    `name`             varchar(50)        NOT NULL COMMENT '宠物昵称',
+    `avatar`           bigint COMMENT '头像',
+    `type`             tinyint  DEFAULT 1 COMMENT '类型 (1:猫, 2:狗, 3:其他)',
+    `breed`            varchar(50) COMMENT '品种',
+    `gender`           tinyint  DEFAULT 1 COMMENT '性别 (1:公, 2:母)',
+    `birthday`         date COMMENT '出生日期',
+    `age`              int      DEFAULT 0 COMMENT '年龄(岁)',
+    `weight`           decimal(5, 2) DEFAULT 0.00 COMMENT '体重(kg)',
+    `size`             varchar(20) DEFAULT 'small' COMMENT '体型 (small:小型, medium:中型, large:大型)',
+    `is_sterilized`    tinyint  DEFAULT 0 COMMENT '是否绝育 (0:否, 1:是)',
+    `arrival_time`     date COMMENT '到家时间',
+    `house_type`       varchar(20) COMMENT '家庭房屋类型',
+    `entry_method`     varchar(20) COMMENT '入门方式',
+    `entry_password`   varchar(50) COMMENT '开门密码',
+    `key_location`     varchar(100) COMMENT '钥匙位置',
+    `personality`      varchar(255) COMMENT '性格关键词',
+    `cute_personality` text COMMENT '萌宠性格描述',
+    `health_status`    varchar(50) DEFAULT '健康' COMMENT '健康状况',
+    `aggression`       tinyint  DEFAULT 0 COMMENT '是否有攻击倾向 (0:否, 1:是)',
+    `vaccine_status`   varchar(50) COMMENT '疫苗状态',
+    `vaccine_cert`     bigint COMMENT '疫苗凭证图片',
+    `medical_history`  text COMMENT '既往病史',
+    `allergies`        text COMMENT '过敏史',
+    `remark`           varchar(500) COMMENT '特别备注',
+    `del_flag`         char(1)  DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
+    `create_dept`      bigint(20) COMMENT '创建部门',
+    `create_by`        bigint(20) COMMENT '创建者',
+    `create_time`      datetime COMMENT '创建时间',
+    `update_by`        bigint(20) COMMENT '更新者',
+    `update_time`      datetime COMMENT '更新时间'
+) ENGINE = innoDB COMMENT = '宠物档案表';
+
+# ============================
+# 标签定义表
+# ============================
+CREATE TABLE `pet_system`.`sys_tag`
+(
+    `id`          bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `name`        varchar(50)        NOT NULL COMMENT '标签名称',
+    `category`    varchar(20)        NOT NULL COMMENT '分类 (user:用户, pet:宠物, fulfiller:履约者)',
+    `color_type`  varchar(20) DEFAULT 'info' COMMENT '颜色样式 (primary, success, warning, danger, info)',
+    `description` varchar(255) COMMENT '标签说明',
+    `type`        tinyint     DEFAULT 2 COMMENT '类型 (1:系统内置, 2:自定义)',
+    `status`      tinyint     DEFAULT 0 COMMENT '状态 (0:启用, 1:停用)',
+    `del_flag`    char(1)     DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
+    `create_dept` bigint(20) COMMENT '创建部门',
+    `create_by`   bigint(20) COMMENT '创建者',
+    `create_time` datetime COMMENT '创建时间',
+    `update_by`   bigint(20) COMMENT '更新者',
+    `update_time` datetime COMMENT '更新时间'
+) ENGINE = innoDB COMMENT = '标签定义表';
+
+# ============================
+# 标签关联表
+# ============================
+CREATE TABLE `pet_system`.`sys_tag_rel`
+(
+    `id`          bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `tag_id`      bigint             NOT NULL COMMENT '标签ID',
+    `target_id`   bigint             NOT NULL COMMENT '目标对象ID',
+    `target_type` varchar(20)        NOT NULL COMMENT '目标类型 (user:用户, pet:宠物, fulfiller:履约者)',
+    UNIQUE KEY `uk_tag_target` (`tag_id`, `target_id`, `target_type`)
+) ENGINE = innoDB COMMENT = '标签关联表';
+
+# ============================
+# 档案变更日志表
+# ============================
+CREATE TABLE `pet_system`.`arc_change_log`
+(
+    `id`            bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `target_id`     bigint             NOT NULL COMMENT '目标对象ID',
+    `target_type`   varchar(20)        NOT NULL COMMENT '目标类型 (user:用户, pet:宠物)',
+    `content`       varchar(500) COMMENT '日志内容',
+    `log_type`      varchar(20) DEFAULT 'remark' COMMENT '日志类型 (remark:备注, system:系统, change:变更)',
+    `operator_id`   bigint COMMENT '操作人ID',
+    `operator_name` varchar(50) COMMENT '操作人名称',
+    `create_time`   datetime COMMENT '创建时间',
+    KEY `idx_target` (`target_id`, `target_type`)
+) ENGINE = innoDB COMMENT = '档案变更日志表';

+ 178 - 0
script/sql/business/fulfiller.sql

@@ -0,0 +1,178 @@
+# 2026/02/27 履约者管理模块
+
+# ============================
+# 履约者信息表
+# ============================
+CREATE TABLE `pet_system`.`flf_fulfiller`
+(
+    `id`            bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `user_id`       bigint COMMENT '关联系统用户ID',
+    `name`          varchar(50)        NOT NULL COMMENT '姓名',
+    `real_name`     varchar(50) COMMENT '身份证真实姓名',
+    `phone`         varchar(20)        NOT NULL COMMENT '手机号/账号',
+    `password`      varchar(100) COMMENT '登录密码',
+    `gender`        tinyint  DEFAULT 1 COMMENT '性别 (1:男, 2:女)',
+    `birthday`      date COMMENT '出生日期',
+    `age`           int      DEFAULT 0 COMMENT '年龄',
+    `avatar`        bigint COMMENT '头像',
+    `id_card`       varchar(20) COMMENT '身份证号',
+    `id_card_front` bigint COMMENT '身份证正面',
+    `id_card_back`  bigint COMMENT '身份证背面',
+    `id_card_expiry` date COMMENT '身份证有效期至',
+    `service_types` varchar(255) COMMENT '服务类型(JSON数组, 如:宠物接送/上门喂遛/上门洗护)',
+    `city_code`     varchar(20) COMMENT '服务城市编码',
+    `city_name`     varchar(100) COMMENT '服务城市名称',
+    `station_id`    bigint COMMENT '归属站点ID',
+    `work_type`     varchar(20) DEFAULT 'part_time' COMMENT '工作性质 (full_time:全职, part_time:兼职)',
+    `level_id`      bigint COMMENT '等级ID',
+    `points`        int      DEFAULT 0 COMMENT '当前积分',
+    `balance`       bigint   DEFAULT 0 COMMENT '账户余额(分)',
+    `status`        varchar(20) DEFAULT 'resting' COMMENT '状态 (resting:休息, busy:接单中, disabled:禁用)',
+    `auth_id`       tinyint  DEFAULT 0 COMMENT '是否身份证认证 (0:否, 1:是)',
+    `auth_qual`     tinyint  DEFAULT 0 COMMENT '是否资质认证 (0:否, 1:是)',
+    `qual_images`   text COMMENT '资质证书图片(JSON)',
+    `order_count`   int      DEFAULT 0 COMMENT '服务单量',
+    `reject_count`  int      DEFAULT 0 COMMENT '拒单量',
+    `rating`        decimal(2, 1) DEFAULT 5.0 COMMENT '综合评分',
+    `del_flag`      char(1)  DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
+    `create_dept`   bigint(20) COMMENT '创建部门',
+    `create_by`     bigint(20) COMMENT '创建者',
+    `create_time`   datetime COMMENT '创建时间',
+    `update_by`     bigint(20) COMMENT '更新者',
+    `update_time`   datetime COMMENT '更新时间'
+) ENGINE = innoDB COMMENT = '履约者信息表';
+
+# ============================
+# 履约者审核记录表
+# ============================
+CREATE TABLE `pet_system`.`flf_audit`
+(
+    `id`              bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `fulfiller_id`    bigint COMMENT '履约者ID(通过后关联)',
+    `type`            varchar(20)        NOT NULL COMMENT '审核类型 (register:入驻, qualification:资质变更)',
+    `name`            varchar(50) COMMENT '申请人姓名',
+    `phone`           varchar(20) COMMENT '申请人手机号',
+    `gender`          tinyint DEFAULT 0 COMMENT '性别 (1:男, 2:女)',
+    `birthday`        date COMMENT '出生日期',
+    `work_type`       varchar(20) COMMENT '工作类型 (full_time:全职, part_time:兼职)',
+    `city`            varchar(100) COMMENT '意向城市',
+    `location`        varchar(255) COMMENT '意向地点',
+    `real_name`       varchar(50) COMMENT '证件真实姓名',
+    `id_card`         varchar(20) COMMENT '身份证号',
+    `id_valid_date`   date COMMENT '证件有效期至',
+    `id_card_front`   bigint COMMENT '身份证人像面',
+    `id_card_back`    bigint COMMENT '身份证国徽面',
+    `qualifications`  text COMMENT '专业资质图片(JSON数组)',
+    `service_types`   varchar(255) COMMENT '申请服务类型(JSON数组)',
+    `status`          tinyint DEFAULT 0 COMMENT '状态 (0:待审核, 1:已通过, 2:已驳回)',
+    `audit_by`        bigint COMMENT '审核人ID',
+    `audit_time`      datetime COMMENT '审核时间',
+    `reject_reason`   varchar(500) COMMENT '驳回原因',
+    `create_time`     datetime COMMENT '申请提交时间',
+    KEY `idx_fulfiller_id` (`fulfiller_id`)
+) ENGINE = innoDB COMMENT = '履约者审核记录表';
+
+# ============================
+# 等级规则表
+# ============================
+CREATE TABLE `pet_system`.`flf_level`
+(
+    `id`                  bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `level`               int                NOT NULL COMMENT '等级数值(Lv.1, Lv.2...)',
+    `name`                varchar(50)        NOT NULL COMMENT '等级名称',
+    `icon`                bigint COMMENT '等级图标',
+    `bg_image`            bigint COMMENT '等级背景图片',
+    `min_points`          int     DEFAULT 0 COMMENT '最低积分',
+    `max_points`          int     DEFAULT -1 COMMENT '最高积分(-1为无上限)',
+    `holiday_multiplier`  decimal(3, 1) DEFAULT 1.0 COMMENT '节假日积分倍率',
+    `upgrade_rules`       text COMMENT '升级规则配置(JSON: periodDays/minOrders/onTimeRate/complaintRate/maxViolations/accumulatedPoints)',
+    `downgrade_type`      varchar(20) DEFAULT 'points' COMMENT '降级规则类型 (points:仅积分不足, strict:不满足即降级)',
+    `deductions`          text COMMENT '扣分配置(JSON: late/violation/complaint)',
+    `status`              tinyint DEFAULT 0 COMMENT '状态 (0:启用, 1:停用)',
+    `sort`                int     DEFAULT 0 COMMENT '排序',
+    `del_flag`            char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
+    `create_dept`         bigint(20) COMMENT '创建部门',
+    `create_by`           bigint(20) COMMENT '创建者',
+    `create_time`         datetime COMMENT '创建时间',
+    `update_by`           bigint(20) COMMENT '更新者',
+    `update_time`         datetime COMMENT '更新时间'
+) ENGINE = innoDB COMMENT = '等级规则表';
+
+# ============================
+# 等级权益表
+# ============================
+CREATE TABLE `pet_system`.`flf_right`
+(
+    `id`          bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `name`        varchar(50)        NOT NULL COMMENT '权益名称',
+    `icon`        bigint COMMENT '权益图标',
+    `description` varchar(500) COMMENT '权益描述',
+    `status`      tinyint DEFAULT 0 COMMENT '状态 (0:启用, 1:停用)',
+    `del_flag`    char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
+    `create_dept` bigint(20) COMMENT '创建部门',
+    `create_by`   bigint(20) COMMENT '创建者',
+    `create_time` datetime COMMENT '创建时间',
+    `update_by`   bigint(20) COMMENT '更新者',
+    `update_time` datetime COMMENT '更新时间'
+) ENGINE = innoDB COMMENT = '等级权益表';
+
+# ============================
+# 等级-权益关联表
+# ============================
+CREATE TABLE `pet_system`.`flf_level_right`
+(
+    `level_id` bigint NOT NULL COMMENT '等级ID',
+    `right_id` bigint NOT NULL COMMENT '权益ID',
+    PRIMARY KEY (`level_id`, `right_id`)
+) ENGINE = innoDB COMMENT = '等级-权益关联表';
+
+# ============================
+# 积分变动记录表
+# ============================
+CREATE TABLE `pet_system`.`flf_points_log`
+(
+    `id`            bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `fulfiller_id`  bigint             NOT NULL COMMENT '履约者ID',
+    `type`          varchar(20)        NOT NULL COMMENT '变动类型 (add:增加, reduce:扣除)',
+    `biz_type`      varchar(20) COMMENT '业务类型 (order:订单, reward:奖励, punish:惩罚, adjust:手动调整)',
+    `amount`        int                NOT NULL COMMENT '变动数值(正数)',
+    `points_after`  int     DEFAULT 0 COMMENT '变动后积分',
+    `reason`        varchar(500) COMMENT '变动原因',
+    `operator_id`   bigint COMMENT '操作人ID',
+    `create_time`   datetime COMMENT '创建时间',
+    KEY `idx_fulfiller_id` (`fulfiller_id`)
+) ENGINE = innoDB COMMENT = '积分变动记录表';
+
+# ============================
+# 余额变动记录表
+# ============================
+CREATE TABLE `pet_system`.`flf_balance_log`
+(
+    `id`            bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `fulfiller_id`  bigint             NOT NULL COMMENT '履约者ID',
+    `type`          varchar(20)        NOT NULL COMMENT '变动方向 (add:增加, reduce:减少)',
+    `sub_type`      varchar(20) COMMENT '资金类型 (reward:奖励, punish:惩罚, salary:工资发放, withdraw:提现, other:其他)',
+    `amount`        bigint             NOT NULL COMMENT '变动金额(分, 正数)',
+    `balance_after` bigint  DEFAULT 0 COMMENT '变动后余额(分)',
+    `reason`        varchar(500) COMMENT '备注说明',
+    `operator_id`   bigint COMMENT '操作人ID',
+    `create_time`   datetime COMMENT '创建时间',
+    KEY `idx_fulfiller_id` (`fulfiller_id`)
+) ENGINE = innoDB COMMENT = '余额变动记录表';
+
+# ============================
+# 奖惩记录表
+# ============================
+CREATE TABLE `pet_system`.`flf_reward_log`
+(
+    `id`            bigint PRIMARY KEY NOT NULL COMMENT '主键ID',
+    `fulfiller_id`  bigint             NOT NULL COMMENT '履约者ID',
+    `type`          varchar(10)        NOT NULL COMMENT '操作类型 (reward:奖励, punish:惩罚)',
+    `target`        varchar(10)        NOT NULL COMMENT '关联项目 (points:积分, balance:余额)',
+    `amount`        int                NOT NULL COMMENT '涉及数值',
+    `reason`        varchar(500) COMMENT '奖惩原因',
+    `operator_id`   bigint COMMENT '操作人ID',
+    `operator_name` varchar(50) COMMENT '操作人名称',
+    `create_time`   datetime COMMENT '创建时间',
+    KEY `idx_fulfiller_id` (`fulfiller_id`)
+) ENGINE = innoDB COMMENT = '奖惩记录表';

+ 70 - 0
script/sql/business/update.sql

@@ -118,3 +118,73 @@ values(2026924472514756613, '短信配置删除', 2026924472514756609, '4',  '#'
 
 insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, platform_id)
 values(2026924472514756614, '短信配置导出', 2026924472514756609, '5',  '#', '', 1, 0, 'F', '0', '0', 'resource:smsConfig:export',       '#', 103, 1, sysdate(), null, null, '', 0);
+
+# 2026/02/27 档案管理模块 - 业务字典
+-- 用户性别复用框架已有字典 sys_user_sex (0:男, 1:女, 2:未知)
+-- 字典类型
+insert into sys_dict_type values(101, '000000', '用户状态',   'sys_customer_status',   103, 1, sysdate(), null, null, '用户档案状态');
+insert into sys_dict_type values(102, '000000', '宠物类型',   'sys_pet_type',          103, 1, sysdate(), null, null, '宠物类型列表');
+insert into sys_dict_type values(103, '000000', '宠物性别',   'sys_pet_gender',        103, 1, sysdate(), null, null, '宠物性别列表');
+insert into sys_dict_type values(104, '000000', '宠物体型',   'sys_pet_size',          103, 1, sysdate(), null, null, '宠物体型列表');
+insert into sys_dict_type values(105, '000000', '房屋类型',   'sys_house_type',        103, 1, sysdate(), null, null, '房屋类型列表');
+insert into sys_dict_type values(106, '000000', '入门方式',   'sys_entry_method',      103, 1, sysdate(), null, null, '入门方式列表');
+insert into sys_dict_type values(107, '000000', '标签颜色',   'sys_tag_color_type',    103, 1, sysdate(), null, null, '标签颜色样式');
+insert into sys_dict_type values(108, '000000', '宠物品种',   'sys_pet_breed',         103, 1, sysdate(), null, null, '宠物品种列表');
+
+-- 字典数据: 用户状态 sys_customer_status
+insert into sys_dict_data values(103, '000000', 1, '正常', '0', 'sys_customer_status', '', 'primary', 'Y', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(104, '000000', 2, '冻结', '1', 'sys_customer_status', '', 'danger',  'N', 103, 1, sysdate(), null, null, '');
+
+-- 字典数据: 宠物类型 sys_pet_type
+insert into sys_dict_data values(105, '000000', 1, '猫',   '1', 'sys_pet_type', '', '',        'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(106, '000000', 2, '狗',   '2', 'sys_pet_type', '', '',        'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(107, '000000', 3, '其他', '3', 'sys_pet_type', '', '',        'N', 103, 1, sysdate(), null, null, '');
+
+-- 字典数据: 宠物性别 sys_pet_gender
+insert into sys_dict_data values(108, '000000', 1, '公', '1', 'sys_pet_gender', '', '',        'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(109, '000000', 2, '母', '2', 'sys_pet_gender', '', '',        'N', 103, 1, sysdate(), null, null, '');
+
+-- 字典数据: 宠物体型 sys_pet_size
+insert into sys_dict_data values(110, '000000', 1, '小型', 'small',  'sys_pet_size', '', '',        'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(111, '000000', 2, '中型', 'medium', 'sys_pet_size', '', '',        'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(112, '000000', 3, '大型', 'large',  'sys_pet_size', '', '',        'N', 103, 1, sysdate(), null, null, '');
+
+-- 字典数据: 房屋类型 sys_house_type
+insert into sys_dict_data values(113, '000000', 1, '楼梯', 'stairs',   'sys_house_type', '', '',        'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(114, '000000', 2, '电梯', 'elevator', 'sys_house_type', '', '',        'N', 103, 1, sysdate(), null, null, '');
+
+-- 字典数据: 入门方式 sys_entry_method
+insert into sys_dict_data values(115, '000000', 1, '密码开门', 'password', 'sys_entry_method', '', '',        'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(116, '000000', 2, '钥匙开门', 'key',      'sys_entry_method', '', '',        'N', 103, 1, sysdate(), null, null, '');
+
+-- 字典数据: 标签颜色 sys_tag_color_type
+insert into sys_dict_data values(117, '000000', 1, '默认',  'info',    'sys_tag_color_type', '', 'info',    'Y', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(118, '000000', 2, '主要',  'primary', 'sys_tag_color_type', '', 'primary', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(119, '000000', 3, '成功',  'success', 'sys_tag_color_type', '', 'success', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(120, '000000', 4, '警告',  'warning', 'sys_tag_color_type', '', 'warning', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(121, '000000', 5, '危险',  'danger',  'sys_tag_color_type', '', 'danger',  'N', 103, 1, sysdate(), null, null, '');
+
+-- 字典数据: 宠物品种 sys_pet_breed (value=品种名称,后续可在字典管理中自行增删)
+insert into sys_dict_data values(122, '000000', 1,  '金毛',           '金毛',           'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(123, '000000', 2,  '拉布拉多',       '拉布拉多',       'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(124, '000000', 3,  '柴犬',           '柴犬',           'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(125, '000000', 4,  '柯基',           '柯基',           'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(126, '000000', 5,  '哈士奇',         '哈士奇',         'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(127, '000000', 6,  '阿拉斯加',       '阿拉斯加',       'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(128, '000000', 7,  '萨摩耶',         '萨摩耶',         'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(129, '000000', 8,  '边境牧羊犬',     '边境牧羊犬',     'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(130, '000000', 9,  '德国牧羊犬',     '德国牧羊犬',     'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(131, '000000', 10, '贵宾犬/泰迪',    '贵宾犬/泰迪',    'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(132, '000000', 11, '比熊',           '比熊',           'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(133, '000000', 12, '博美',           '博美',           'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(134, '000000', 13, '雪纳瑞',         '雪纳瑞',         'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(135, '000000', 14, '法斗',           '法斗',           'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(136, '000000', 15, '中华田园犬',     '中华田园犬',     'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(137, '000000', 16, '英短',           '英短',           'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(138, '000000', 17, '美短',           '美短',           'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(139, '000000', 18, '布偶猫',         '布偶猫',         'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(140, '000000', 19, '加菲猫',         '加菲猫',         'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(141, '000000', 20, '暹罗猫',         '暹罗猫',         'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(142, '000000', 21, '波斯猫',         '波斯猫',         'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(143, '000000', 22, '缅因猫',         '缅因猫',         'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');
+insert into sys_dict_data values(144, '000000', 23, '中华田园猫',     '中华田园猫',     'sys_pet_breed', '', '', 'N', 103, 1, sysdate(), null, null, '');

+ 1 - 0
script/sql/ry-cloud.sql

@@ -157,6 +157,7 @@ create table sys_user (
   update_by         bigint(20)      default null               comment '更新者',
   update_time       datetime                                   comment '更新时间',
   remark            varchar(500)    default null               comment '备注',
+  platform_id       int(11)         default null               comment '平台ID 当前用户属于哪一个平台',
   primary key (user_id)
 ) engine=innodb comment = '用户信息表';