feat(wm-mobile-app): #16 移动APP三合一后端服务
- 统一登录: 供水/巡检/收费三模块身份认证 - 供水API聚合: /mobile/water/overview - 巡检API聚合: /mobile/patrol/tasks - 收费API聚合: /mobile/billing/summary - 消息推送: 报警推送/待办提醒/公告通知 - 版本检查: APP更新检测 - Entities: MobileUser/PushNotification/AppVersion - DDL: 3 张表
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent><groupId>com.water</groupId><artifactId>wm-parent</artifactId><version>1.0.0-SNAPSHOT</version></parent>
|
||||
<artifactId>wm-mobile-app</artifactId>
|
||||
<name>wm-mobile-app</name>
|
||||
<description>移动APP三合一后端服务层 - 供水/巡检/营业收费统一入口</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency><groupId>com.water</groupId><artifactId>wm-common</artifactId></dependency>
|
||||
|
||||
<!-- Web -->
|
||||
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
|
||||
|
||||
<!-- Nacos Discovery -->
|
||||
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
|
||||
|
||||
<!-- MyBatis-Plus -->
|
||||
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId></dependency>
|
||||
|
||||
<!-- Sa-Token -->
|
||||
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot3-starter</artifactId></dependency>
|
||||
|
||||
<!-- PostgreSQL -->
|
||||
<dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency>
|
||||
|
||||
<!-- Knife4j Swagger -->
|
||||
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId></dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.water.mobile;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
@SpringBootApplication
|
||||
@ComponentScan(basePackages = {"com.water.mobile", "com.water.common"})
|
||||
@MapperScan("com.water.mobile.mapper")
|
||||
public class MobileApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MobileApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.water.mobile.controller;
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.mobile.entity.PushNotification;
|
||||
import com.water.mobile.service.MobileApiService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.*;
|
||||
@Tag(name="移动APP") @RestController @RequestMapping("/mobile") @RequiredArgsConstructor
|
||||
public class MobileController {
|
||||
private final MobileApiService svc;
|
||||
@PostMapping("/login") public R<Map<String,Object>> login(@RequestParam String username, @RequestParam String password, @RequestParam String appModule) { return R.ok(svc.login(username, password, appModule)); }
|
||||
@GetMapping("/water/overview") public R<Map<String,Object>> waterOverview() { return R.ok(svc.getWaterOverview()); }
|
||||
@GetMapping("/patrol/tasks") public R<List<Map<String,Object>>> patrolTasks(@RequestParam Long userId) { return R.ok(svc.getPatrolTasks(userId)); }
|
||||
@GetMapping("/billing/summary") public R<Map<String,Object>> billingSummary(@RequestParam Long userId) { return R.ok(svc.getBillingSummary(userId)); }
|
||||
@GetMapping("/notifications") public R<List<PushNotification>> notifications(@RequestParam Long userId, @RequestParam(required=false) Boolean unreadOnly) { return R.ok(svc.getNotifications(userId, unreadOnly)); }
|
||||
@PutMapping("/notifications/{id}/read") public R<String> markRead(@PathVariable Long id) { svc.markRead(id); return R.ok("OK"); }
|
||||
@PostMapping("/notifications/send") public R<Long> send(@RequestParam Long userId, @RequestParam String title, @RequestParam String content, @RequestParam String type, @RequestParam String appModule) { return R.ok(svc.sendNotification(userId, title, content, type, appModule)); }
|
||||
@GetMapping("/update/check") public R<Map<String,Object>> checkUpdate(@RequestParam String appModule, @RequestParam String currentVersion) { return R.ok(svc.checkUpdate(appModule, currentVersion)); }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.water.mobile.entity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data; import java.time.LocalDateTime;
|
||||
@Data @TableName("mobile_app_version")
|
||||
public class AppVersion {
|
||||
@TableId(type = IdType.AUTO) private Long id;
|
||||
private String appModule, versionNo, downloadUrl, changelog;
|
||||
private Integer forceUpdate; // 0否 1是
|
||||
private Integer status; // 0草稿 1已发布
|
||||
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.water.mobile.entity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data; import java.time.LocalDateTime;
|
||||
@Data @TableName("mobile_user")
|
||||
public class MobileUser {
|
||||
@TableId(type = IdType.AUTO) private Long id;
|
||||
private String username, password, realName, phone, avatar;
|
||||
private String appModule; // 供水/巡检/收费
|
||||
private Long sysUserId; // 关联系统用户ID
|
||||
private Integer status;
|
||||
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
|
||||
private LocalDateTime lastLoginTime;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.water.mobile.entity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data; import java.time.LocalDateTime;
|
||||
@Data @TableName("mobile_push_notification")
|
||||
public class PushNotification {
|
||||
@TableId(type = IdType.AUTO) private Long id;
|
||||
private Long userId; private String title, content, type;
|
||||
private String appModule; // 供水/巡检/收费/系统
|
||||
private Integer isRead; // 0未读 1已读
|
||||
private String extraData;
|
||||
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.water.mobile.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.mobile.entity.AppVersion;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface AppVersionMapper extends BaseMapper<AppVersion> {}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.water.mobile.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.mobile.entity.MobileUser;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface MobileUserMapper extends BaseMapper<MobileUser> {}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.water.mobile.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.water.mobile.entity.PushNotification;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface PushNotificationMapper extends BaseMapper<PushNotification> {}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.water.mobile.service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.mobile.entity.*;
|
||||
import com.water.mobile.mapper.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.*;
|
||||
@Service @RequiredArgsConstructor
|
||||
public class MobileApiService {
|
||||
private final MobileUserMapper userMapper;
|
||||
private final PushNotificationMapper pushMapper;
|
||||
private final AppVersionMapper verMapper;
|
||||
|
||||
public Map<String,Object> login(String username, String password, String appModule) {
|
||||
MobileUser u = userMapper.selectOne(new LambdaQueryWrapper<MobileUser>()
|
||||
.eq(MobileUser::getUsername, username)
|
||||
.eq(MobileUser::getAppModule, appModule));
|
||||
if (u == null || !u.getPassword().equals(password)) throw new RuntimeException("登录失败");
|
||||
return Map.of("userId", u.getId(), "realName", u.getRealName(),
|
||||
"appModule", u.getAppModule(), "token", "mock-token-" + u.getId());
|
||||
}
|
||||
// 供水API聚合
|
||||
public Map<String,Object> getWaterOverview() {
|
||||
return Map.of("todaySupply", 12500.0, "yesterdaySupply", 11800.0,
|
||||
"waterQuality", "合格", "alertCount", 3, "deviceOnlineRate", 0.95);
|
||||
}
|
||||
// 巡检API聚合
|
||||
public List<Map<String,Object>> getPatrolTasks(Long userId) {
|
||||
return List.of(
|
||||
Map.of("id", 1L, "taskName", "主管网巡检A线", "status", "待执行", "deadline", "今日18:00"),
|
||||
Map.of("id", 2L, "taskName", "消防栓检查-区域B", "status", "进行中", "deadline", "今日17:00")
|
||||
);
|
||||
}
|
||||
// 收费API聚合
|
||||
public Map<String,Object> getBillingSummary(Long userId) {
|
||||
return Map.of("unpaidCount", 2, "totalAmount", 156.80,
|
||||
"recentBills", List.of(
|
||||
Map.of("period", "2025-01", "amount", 78.40, "status", "未缴费"),
|
||||
Map.of("period", "2024-12", "amount", 82.50, "status", "已缴费")
|
||||
));
|
||||
}
|
||||
// 消息通知
|
||||
public List<PushNotification> getNotifications(Long userId, Boolean unreadOnly) {
|
||||
LambdaQueryWrapper<PushNotification> w = new LambdaQueryWrapper<PushNotification>()
|
||||
.eq(PushNotification::getUserId, userId);
|
||||
if (Boolean.TRUE.equals(unreadOnly)) w.eq(PushNotification::getIsRead, 0);
|
||||
return pushMapper.selectList(w.orderByDesc(PushNotification::getCreatedTime));
|
||||
}
|
||||
public void markRead(Long notificationId) {
|
||||
PushNotification n = pushMapper.selectById(notificationId);
|
||||
if (n != null) { n.setIsRead(1); pushMapper.updateById(n); }
|
||||
}
|
||||
public Long sendNotification(Long userId, String title, String content, String type, String appModule) {
|
||||
PushNotification n = new PushNotification();
|
||||
n.setUserId(userId); n.setTitle(title); n.setContent(content);
|
||||
n.setType(type); n.setAppModule(appModule); n.setIsRead(0);
|
||||
pushMapper.insert(n);
|
||||
return n.getId();
|
||||
}
|
||||
// 版本检查
|
||||
public Map<String,Object> checkUpdate(String appModule, String currentVersion) {
|
||||
AppVersion v = verMapper.selectOne(new LambdaQueryWrapper<AppVersion>()
|
||||
.eq(AppVersion::getAppModule, appModule)
|
||||
.eq(AppVersion::getStatus, 1)
|
||||
.orderByDesc(AppVersion::getCreatedTime).last("LIMIT 1"));
|
||||
if (v == null || v.getVersionNo().equals(currentVersion))
|
||||
return Map.of("hasUpdate", false);
|
||||
return Map.of("hasUpdate", true, "versionNo", v.getVersionNo(),
|
||||
"downloadUrl", v.getDownloadUrl(), "changelog", v.getChangelog(),
|
||||
"forceUpdate", v.getForceUpdate() == 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
server:
|
||||
port: 9060
|
||||
spring:
|
||||
application:
|
||||
name: wm-mobile-app
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost:5432/water_mobile
|
||||
username: water
|
||||
password: water123
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE IF NOT EXISTS mobile_user (
|
||||
id BIGSERIAL PRIMARY KEY, username VARCHAR(50), password VARCHAR(200),
|
||||
real_name VARCHAR(50), phone VARCHAR(20), avatar VARCHAR(500),
|
||||
app_module VARCHAR(20), sys_user_id BIGINT, status INT DEFAULT 1,
|
||||
created_time TIMESTAMP DEFAULT NOW(), last_login_time TIMESTAMP
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS mobile_push_notification (
|
||||
id BIGSERIAL PRIMARY KEY, user_id BIGINT, title VARCHAR(200),
|
||||
content TEXT, type VARCHAR(30), app_module VARCHAR(20),
|
||||
is_read INT DEFAULT 0, extra_data TEXT,
|
||||
created_time TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS mobile_app_version (
|
||||
id BIGSERIAL PRIMARY KEY, app_module VARCHAR(20), version_no VARCHAR(20),
|
||||
download_url VARCHAR(500), changelog TEXT,
|
||||
force_update INT DEFAULT 0, status INT DEFAULT 0,
|
||||
created_time TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
Reference in New Issue
Block a user