chore: checkpoint before switching to main
This commit is contained in:
+1
-1
@@ -10,4 +10,4 @@ FROM eclipse-temurin:21-jre-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/target/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java", "-jar", "/app/app.jar", "--spring.profiles.active=${SPRING_PROFILES_ACTIVE:docker}"]
|
||||
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
|
||||
|
||||
+6
-2
@@ -162,8 +162,12 @@ public class RelationshipService {
|
||||
com.danyang.match.platform.relationship.entity.UserRelation r2 = relationMapper.findByRelation(targetUserId, userId, Constants.RELATION_TYPE_LIKED).orElse(null);
|
||||
if (r2 != null) relationMapper.deleteById(r2.getId());
|
||||
|
||||
// 解除已有的Match
|
||||
unmatch(userId, targetUserId);
|
||||
// 仅解除已有 Match;没有 Match 不能导致拉黑事务回滚。
|
||||
matchMapper.findActiveMatch(userId, targetUserId).ifPresent(match -> {
|
||||
match.setStatus(Constants.MATCH_STATUS_UNMATCHED);
|
||||
match.setUnmatchedAt(Instant.now());
|
||||
matchMapper.updateById(match);
|
||||
});
|
||||
|
||||
log.info("拉黑用户: userId={}, targetUserId={}", userId, targetUserId);
|
||||
saveAuditLog(userId, "BLOCK", "user_relation", targetUserId, null, null);
|
||||
|
||||
+7
-17
@@ -187,33 +187,23 @@ public class RelationshipStatusService {
|
||||
}
|
||||
|
||||
String reqStatus = request.getStatus();
|
||||
if (Constants.SUCCESS_REQUEST_STATUS_CONFIRMED.equals(reqStatus)) {
|
||||
throw new GlobalExceptionHandler.BusinessException(
|
||||
ApiResponseCode.CONFLICT, "该成功牵手请求已确认");
|
||||
}
|
||||
if (Constants.SUCCESS_REQUEST_STATUS_DECLINED.equals(reqStatus)) {
|
||||
throw new GlobalExceptionHandler.BusinessException(
|
||||
ApiResponseCode.CONFLICT, "该成功牵手邀请已被拒绝");
|
||||
}
|
||||
|
||||
// 3. 查找另一方的确认状态
|
||||
// 发起方调用时: otherUserId = targetUserId(对方)
|
||||
// 响应方调用时: otherUserId = initiatedBy(发起方)
|
||||
boolean isInitiator = userId.equals(request.getInitiatedBy());
|
||||
String otherUserId = isInitiator ? targetUserId : request.getInitiatedBy();
|
||||
RelationshipSuccessRequest otherConfirm = successRequestMapper.selectOne(
|
||||
new LambdaQueryWrapper<RelationshipSuccessRequest>()
|
||||
.eq(RelationshipSuccessRequest::getMatchId, match.getId())
|
||||
.eq(RelationshipSuccessRequest::getInitiatedBy, otherUserId)
|
||||
.eq(RelationshipSuccessRequest::getStatus, Constants.SUCCESS_REQUEST_STATUS_CONFIRMED)
|
||||
.isNull(RelationshipSuccessRequest::getDeletedAt)
|
||||
.last("LIMIT 1"));
|
||||
boolean secondConfirmation = Constants.SUCCESS_REQUEST_STATUS_CONFIRMED.equals(reqStatus);
|
||||
if (secondConfirmation && userId.equals(request.getConfirmedBy())) {
|
||||
throw new GlobalExceptionHandler.BusinessException(
|
||||
ApiResponseCode.CONFLICT, "您已确认过该成功牵手请求");
|
||||
}
|
||||
String otherUserId = userId.equals(match.getUserAId()) ? match.getUserBId() : match.getUserAId();
|
||||
|
||||
// 5. 标记当前用户已确认
|
||||
request.setConfirmedBy(userId);
|
||||
request.setResolvedAt(Instant.now());
|
||||
|
||||
if (otherConfirm != null) {
|
||||
if (secondConfirmation) {
|
||||
// ===== 双方都已确认 → 执行成功牵手 =====
|
||||
match.setStatus(Constants.MATCH_STATUS_SUCCESSFUL);
|
||||
match.setSuccessfulAt(Instant.now());
|
||||
|
||||
@@ -33,7 +33,7 @@ object-storage:
|
||||
signature-version: s3v4
|
||||
|
||||
verification:
|
||||
provider: mock
|
||||
provider: ${VERIFICATION_PROVIDER:mock}
|
||||
api-url: ${VERIFICATION_API_URL:}
|
||||
api-key: ${VERIFICATION_API_KEY:}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
logging:
|
||||
level:
|
||||
com.danyang.match.platform: INFO
|
||||
org.mybatis: WARN
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: false
|
||||
swagger-ui:
|
||||
enabled: false
|
||||
|
||||
spring:
|
||||
security:
|
||||
user:
|
||||
name: disabled
|
||||
password: ${SPRING_SECURITY_FALLBACK_PASSWORD}
|
||||
@@ -0,0 +1,26 @@
|
||||
-- 对齐继承 BaseEntity 的消息/推荐实体,避免 MyBatis-Plus 读写不存在的公共列。
|
||||
ALTER TABLE relationship_success_requests
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE recommendation_records
|
||||
ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE conversations ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE conversation_members
|
||||
ADD COLUMN IF NOT EXISTS id UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_conversation_members_id ON conversation_members(id);
|
||||
|
||||
ALTER TABLE messages
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
ALTER TABLE notifications
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
+14
-13
@@ -11,10 +11,12 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* P25-T02: Migration 最终验证
|
||||
* 验证所有 V001-V029 迁移文件存在、命名规范、语法正确
|
||||
* 验证所有 V001-V033 迁移文件存在、命名规范、语法正确
|
||||
*/
|
||||
class MigrationValidationTest {
|
||||
|
||||
private static final int LATEST_MIGRATION = 33;
|
||||
|
||||
private static final String MIGRATION_DIR =
|
||||
"backend/src/main/resources/db/migration/";
|
||||
|
||||
@@ -22,8 +24,7 @@ class MigrationValidationTest {
|
||||
|
||||
@Test
|
||||
void testAllMigrationsExist() {
|
||||
// 预期 V001-V029
|
||||
for (int i = 1; i <= 29; i++) {
|
||||
for (int i = 1; i <= LATEST_MIGRATION; i++) {
|
||||
String expected = String.format("V%03d__", i);
|
||||
assertTrue(filesStartWith(expected),
|
||||
"迁移文件缺失: " + expected + "*.sql");
|
||||
@@ -47,10 +48,10 @@ class MigrationValidationTest {
|
||||
"命名不规范: " + name);
|
||||
// 版本号递增
|
||||
int version = Integer.parseInt(name.substring(1, 4));
|
||||
assertTrue(version >= 1 && version <= 29,
|
||||
"版本号应在1-29之间: " + name);
|
||||
assertTrue(version >= 1 && version <= LATEST_MIGRATION,
|
||||
"版本号超出当前基线: " + name);
|
||||
}
|
||||
assertEquals(29, count, "应有29个迁移文件,实际" + count + "个");
|
||||
assertEquals(LATEST_MIGRATION, count, "迁移文件数量不符合当前基线");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +68,8 @@ class MigrationValidationTest {
|
||||
content.contains("CREATE INDEX") ||
|
||||
content.contains("INSERT INTO") ||
|
||||
content.contains("CREATE TYPE");
|
||||
assertTrue(hasDDL, "迁移文件缺少DDL语句: " + file.getFileName());
|
||||
assertTrue(hasDDL || content.contains("占位"),
|
||||
"迁移文件缺少DDL语句且未声明为占位: " + file.getFileName());
|
||||
|
||||
// 不应包含生产密钥
|
||||
assertFalse(content.toLowerCase().contains("password:") &&
|
||||
@@ -98,12 +100,11 @@ class MigrationValidationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testV007MatchConstraints() throws IOException {
|
||||
// V007 应包含 Match 唯一约束
|
||||
String v007 = readMigration("V007");
|
||||
assertTrue(v007.contains("uk_match_pair") || v007.contains("UNIQUE(user_a_id"),
|
||||
"V007 应包含 Match 唯一约束");
|
||||
assertTrue(v007.contains("chk_match_status"), "V007 应包含 Match 状态检查约束");
|
||||
void testMatchConstraints() throws IOException {
|
||||
String v003 = readMigration("V003");
|
||||
assertTrue(v003.contains("uk_match_pair") || v003.contains("UNIQUE(user_a_id"),
|
||||
"关系表迁移应包含 Match 唯一约束");
|
||||
assertTrue(v003.contains("chk_match_status"), "关系表迁移应包含 Match 状态检查约束");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+25
-53
@@ -5,6 +5,8 @@ import com.danyang.match.platform.audit.mapper.AuditLogMapper;
|
||||
import com.danyang.match.platform.messaging.mapper.ConversationMapper;
|
||||
import com.danyang.match.platform.messaging.mapper.ConversationMemberMapper;
|
||||
import com.danyang.match.platform.messaging.mapper.MessageMapper;
|
||||
import com.danyang.match.platform.activity.entity.ActivityRegistration;
|
||||
import com.danyang.match.platform.activity.mapper.ActivityRegistrationMapper;
|
||||
import com.danyang.match.platform.messaging.service.MessagingService;
|
||||
import com.danyang.match.platform.relationship.entity.Match;
|
||||
import com.danyang.match.platform.relationship.entity.RelationshipSuccessRequest;
|
||||
@@ -38,6 +40,10 @@ class P0FixVerificationTest {
|
||||
@Autowired private UserRelationMapper relationMapper;
|
||||
@Autowired private MatchMapper matchMapper;
|
||||
@Autowired private ConversationMapper conversationMapper;
|
||||
@Autowired private ConversationMemberMapper conversationMemberMapper;
|
||||
@Autowired private MessageMapper messageMapper;
|
||||
@Autowired private RelationshipSuccessRequestMapper successRequestMapper;
|
||||
@Autowired private ActivityRegistrationMapper registrationMapper;
|
||||
@Autowired private MessagingService messagingService;
|
||||
@Autowired private RelationshipService relationshipService;
|
||||
@Autowired private RelationshipStatusService statusService;
|
||||
@@ -55,6 +61,10 @@ class P0FixVerificationTest {
|
||||
|
||||
@AfterEach
|
||||
void teardown() {
|
||||
messageMapper.delete(null);
|
||||
conversationMemberMapper.delete(null);
|
||||
conversationMapper.delete(null);
|
||||
successRequestMapper.delete(null);
|
||||
// 清理测试数据
|
||||
List<UserRelation> relations = relationMapper.selectList(
|
||||
new LambdaQueryWrapper<UserRelation>()
|
||||
@@ -86,57 +96,15 @@ class P0FixVerificationTest {
|
||||
@Test
|
||||
@DisplayName("INV-003: Block后A不可向B发送消息")
|
||||
void testBlockPreventsChat() {
|
||||
// 1. A Block B
|
||||
relationshipService.block(userA, userB);
|
||||
|
||||
// 2. 验证Block关系存在
|
||||
assertTrue(relationMapper.findByRelation(userA, userB, Constants.RELATION_TYPE_BLOCKED).isPresent(),
|
||||
"A→B的Block关系应存在");
|
||||
|
||||
// 3. 双向Like创建Match → 创建Conversation
|
||||
relationshipService.like(userA, userB);
|
||||
relationshipService.like(userB, userA);
|
||||
// Match已创建,Conversation未自动创建(P0-4修复前),需要手动创建
|
||||
// 但Block后即使有Match,发消息也应被拒绝
|
||||
String conversationId = conversationMapper.selectList(null).get(0).getId();
|
||||
|
||||
// 4. 尝试发消息应抛异常
|
||||
try {
|
||||
// 先创建Match和Conversation
|
||||
Match match = matchMapper.findActiveMatch(userA, userB).orElse(null);
|
||||
if (match != null) {
|
||||
messagingService.createConversation(match.getId(), userA, userB);
|
||||
}
|
||||
// 获取conversationId
|
||||
// 然后尝试发消息
|
||||
// 由于isBlocked()已修复,应抛出"对方已将你拉黑"
|
||||
// 这里我们直接测试isBlocked逻辑
|
||||
// (完整流程需更多DB操作,此处验证核心逻辑)
|
||||
} catch (RuntimeException e) {
|
||||
// 预期异常
|
||||
}
|
||||
relationshipService.block(userA, userB);
|
||||
|
||||
// 直接验证isBlocked逻辑:通过MessagingService内部方法
|
||||
// 由于isBlocked是private,我们通过getConversation的canSend字段验证
|
||||
// 先创建Conversation
|
||||
Match match = matchMapper.findActiveMatch(userA, userB).orElse(null);
|
||||
if (match == null) {
|
||||
// 手动创建Match
|
||||
match = new Match();
|
||||
String userA_str = userA, userB_str = userB;
|
||||
String u1 = userA_str.compareTo(userB_str) < 0 ? userA_str : userB_str;
|
||||
String u2 = userA_str.compareTo(userB_str) < 0 ? userB_str : userA_str;
|
||||
match.setUserAId(u1);
|
||||
match.setUserBId(u2);
|
||||
match.setStatus(Constants.MATCH_STATUS_ACTIVE);
|
||||
match.setSource(Constants.RELATION_TYPE_LIKED);
|
||||
match.setMatchedAt(java.time.Instant.now());
|
||||
matchMapper.insert(match);
|
||||
messagingService.createConversation(match.getId(), userA, userB);
|
||||
}
|
||||
|
||||
// 验证A的canSend为false(因为B已Block A)
|
||||
// 实际上A Block B,所以B发消息给A应该被阻止
|
||||
// 让B发A: A Block B → B不能发A
|
||||
assertTrue(relationMapper.findByRelation(userA, userB, Constants.RELATION_TYPE_BLOCKED).isPresent());
|
||||
assertThrows(RuntimeException.class,
|
||||
() -> messagingService.sendMessage(userB, conversationId, "测试消息", "blocked-message"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -213,11 +181,13 @@ class P0FixVerificationTest {
|
||||
@Test
|
||||
@DisplayName("P0-3: 查询条件正确过滤当前用户")
|
||||
void testWithdrawQueryFiltersCurrentUser() {
|
||||
// 验证ActivityRegistration查询只查当前用户的记录
|
||||
// 通过代码审查确认:new LambdaQueryWrapper<ActivityRegistration>()
|
||||
// .eq(ActivityRegistration::getUserId, userId)
|
||||
// .in(ActivityRegistration::getStatus, pending, approved)
|
||||
assertTrue(true); // 通过代码审查验证
|
||||
ActivityRegistration registration = new ActivityRegistration();
|
||||
registration.setUserId(userB);
|
||||
registration.setActivityId(java.util.UUID.randomUUID().toString());
|
||||
registration.setStatus(Constants.REGISTRATION_STATUS_PENDING);
|
||||
registrationMapper.insert(registration);
|
||||
|
||||
assertEquals(Boolean.TRUE, accountLifecycleService.checkCanWithdraw(userA).get("canWithdraw"));
|
||||
}
|
||||
|
||||
// ==================== P0-4: Match自动建Conversation验证 ====================
|
||||
@@ -227,7 +197,9 @@ class P0FixVerificationTest {
|
||||
void testMatchAutoCreatesConversation() {
|
||||
// 1. A和B双向Like
|
||||
var resultA = relationshipService.like(userA, userB);
|
||||
assertTrue((Boolean) resultA.get("mutual"), "A→B Like后应mutual=true");
|
||||
assertFalse((Boolean) resultA.get("mutual"), "首次单向 Like 不应形成互选");
|
||||
var resultB = relationshipService.like(userB, userA);
|
||||
assertTrue((Boolean) resultB.get("mutual"), "第二方 Like 后应形成互选");
|
||||
|
||||
// 2. 验证Match存在
|
||||
Match match = matchMapper.findActiveMatch(userA, userB).orElse(null);
|
||||
|
||||
@@ -6,6 +6,9 @@ spring:
|
||||
password:
|
||||
flyway:
|
||||
enabled: false
|
||||
sql:
|
||||
init:
|
||||
schema-locations: classpath:schema-h2.sql
|
||||
jackson:
|
||||
serialization:
|
||||
write-dates-as-timestamps: false
|
||||
@@ -29,4 +32,9 @@ object-storage:
|
||||
secret-key: unit-test-secret-key
|
||||
bucket-public: unit-test-public
|
||||
bucket-private: unit-test-private
|
||||
region: us-east-1
|
||||
region: us-east-1
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.danyang.match.platform: INFO
|
||||
org.mybatis: WARN
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
DROP ALL OBJECTS;
|
||||
|
||||
CREATE TABLE users (
|
||||
id VARCHAR(36) PRIMARY KEY, wechat_openid VARCHAR(128) NOT NULL UNIQUE,
|
||||
wechat_unionid VARCHAR(128), phone VARCHAR(32), account_status VARCHAR(32),
|
||||
relationship_status VARCHAR(32), recommendation_enabled BOOLEAN,
|
||||
last_login_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE user_relations (
|
||||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), target_user_id VARCHAR(36), relation_type VARCHAR(32),
|
||||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
UNIQUE (user_id, target_user_id, relation_type)
|
||||
);
|
||||
CREATE TABLE matches (
|
||||
id VARCHAR(36) PRIMARY KEY, user_a_id VARCHAR(36), user_b_id VARCHAR(36), status VARCHAR(32), source VARCHAR(32),
|
||||
matched_at TIMESTAMP WITH TIME ZONE, unmatched_at TIMESTAMP WITH TIME ZONE, successful_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE relationship_success_requests (
|
||||
id VARCHAR(36) PRIMARY KEY, match_id VARCHAR(36), initiated_by VARCHAR(36), status VARCHAR(32), confirmed_by VARCHAR(36),
|
||||
resolved_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE conversations (
|
||||
id VARCHAR(36) PRIMARY KEY, match_id VARCHAR(36) UNIQUE, status VARCHAR(32), last_message_id VARCHAR(36),
|
||||
last_message_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE conversation_members (
|
||||
id VARCHAR(36) PRIMARY KEY, conversation_id VARCHAR(36), user_id VARCHAR(36), last_read_message_id VARCHAR(36),
|
||||
unread_count INT, can_send BOOLEAN, created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
UNIQUE (conversation_id, user_id)
|
||||
);
|
||||
CREATE TABLE messages (
|
||||
id VARCHAR(36) PRIMARY KEY, conversation_id VARCHAR(36), sender_id VARCHAR(36), client_message_id VARCHAR(128),
|
||||
message_type VARCHAR(32), content CLOB, file_id VARCHAR(36), status VARCHAR(32), risk_level VARCHAR(16),
|
||||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE audit_logs (
|
||||
id VARCHAR(36) PRIMARY KEY, operator_id VARCHAR(36), organization_id VARCHAR(36), action VARCHAR(128),
|
||||
resource_type VARCHAR(64), resource_id VARCHAR(128), before_data CLOB, after_data CLOB, ip VARCHAR(64),
|
||||
user_agent VARCHAR(500), created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE notifications (
|
||||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), type VARCHAR(32), title VARCHAR(200), content VARCHAR(2000),
|
||||
business_type VARCHAR(32), business_id VARCHAR(128), read_status VARCHAR(16), read_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE recommendation_records (
|
||||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), target_user_id VARCHAR(36), source VARCHAR(32), score NUMERIC(5,2),
|
||||
level VARCHAR(16), reasons CLOB, shown_at TIMESTAMP WITH TIME ZONE, action VARCHAR(32), action_at TIMESTAMP WITH TIME ZONE,
|
||||
rule_version INT, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE activity_registrations (
|
||||
id VARCHAR(36) PRIMARY KEY, activity_id VARCHAR(36), user_id VARCHAR(36), status VARCHAR(32),
|
||||
registered_at TIMESTAMP WITH TIME ZONE, reviewed_by VARCHAR(36), reviewed_at TIMESTAMP WITH TIME ZONE,
|
||||
reject_reason VARCHAR(500), checkin_status VARCHAR(32), checkin_at TIMESTAMP WITH TIME ZONE,
|
||||
emergency_contact_name VARCHAR(100), emergency_contact_phone_enc VARCHAR(500), source VARCHAR(64),
|
||||
request_source VARCHAR(64), created_by_operator_id VARCHAR(36), user_confirmed_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE service_appointments (
|
||||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), service_location_id VARCHAR(36), service_type VARCHAR(64),
|
||||
preferred_date DATE, preferred_time_slot VARCHAR(32), user_note CLOB, status VARCHAR(32),
|
||||
assigned_operator_id VARCHAR(36), confirmed_start_at TIMESTAMP WITH TIME ZONE,
|
||||
confirmed_end_at TIMESTAMP WITH TIME ZONE, cancelled_by VARCHAR(36), cancel_reason VARCHAR(500),
|
||||
source VARCHAR(64), request_source VARCHAR(64), created_by_operator_id VARCHAR(36),
|
||||
user_confirmed_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE counseling_services (
|
||||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36), service_type VARCHAR(64), request_source VARCHAR(64),
|
||||
user_request_summary CLOB, organization_id VARCHAR(36), assigned_operator_id VARCHAR(36), status VARCHAR(32),
|
||||
preferred_service_mode VARCHAR(32), preferred_time VARCHAR(128), scheduled_start_at TIMESTAMP WITH TIME ZONE,
|
||||
scheduled_end_at TIMESTAMP WITH TIME ZONE, consent_version VARCHAR(32), consented_at TIMESTAMP WITH TIME ZONE,
|
||||
completed_at TIMESTAMP WITH TIME ZONE, closed_reason VARCHAR(500), companion_user_id VARCHAR(36),
|
||||
created_by_operator_id VARCHAR(36), user_confirmed_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE service_program_members (
|
||||
id VARCHAR(36) PRIMARY KEY, program_id VARCHAR(36), user_id VARCHAR(36), status VARCHAR(32),
|
||||
applied_at TIMESTAMP WITH TIME ZONE, approved_at TIMESTAMP WITH TIME ZONE, approved_by VARCHAR(36),
|
||||
rejected_reason VARCHAR(500), created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
CREATE TABLE user_local_eligibilities (
|
||||
id VARCHAR(36) PRIMARY KEY, user_id VARCHAR(36) UNIQUE, eligibility_type VARCHAR(32),
|
||||
declared_region_code VARCHAR(32), verification_status VARCHAR(32), review_task_id VARCHAR(36),
|
||||
verified_at TIMESTAMP WITH TIME ZONE, verified_by VARCHAR(36), created_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
Reference in New Issue
Block a user