fix: 云超服待修复清单 #1/#3 + 园区成员公司列表 + main 修复

- #1 站内消息:新增 GET /opc/messages/unread-count(红点角标)、POST /opc/messages/read(批量/全部已读),MessageRepository 补 unread_count/mark_read
- #3 发布活动 500:events 表补 created_at 列(models.Event + alembic 0072 迁移,旧库需执行 alembic upgrade head)
- rbac_admin 园区成员接口补充所属公司列表 companies(含 is_admin)
- main.py 修复 root() 语法错误,APP_NAME 更新为云南省超级个体服务平台
- 补充 nginx ssl 证书(opc.pinesound.cn)
This commit is contained in:
Pine
2026-09-09 04:50:08 +08:00
parent 5328835dbe
commit 11a9ad0105
7 changed files with 212 additions and 4 deletions
@@ -0,0 +1,43 @@
"""events 表补充 created_at 列(发布活动按此排序,修复发布活动 500)。
- 旧库 events 表无 created_at 列,create_activity 写入时触发
Unknown column 'created_at' → 500。
- 本迁移对已有表补可空列;新建表由 models.Event 定义直接包含。
"""
from __future__ import annotations
import sqlalchemy as sa
from alembic import op
revision = "0072_events_add_created_at"
down_revision = "0071_ent_onboard_tables"
branch_labels = None
depends_on = None
def _column_exists(bind, table: str, column: str) -> bool:
return bool(
bind.execute(
sa.text(
"SELECT COUNT(*) FROM information_schema.COLUMNS "
"WHERE TABLE_SCHEMA = DATABASE() "
"AND TABLE_NAME = :t AND COLUMN_NAME = :c"
),
{"t": table, "c": column},
).scalar()
)
def upgrade() -> None:
bind = op.get_bind()
if not _column_exists(bind, "events", "created_at"):
op.add_column(
"events",
sa.Column("created_at", sa.String(40), nullable=True, default=""),
)
def downgrade() -> None:
bind = op.get_bind()
if _column_exists(bind, "events", "created_at"):
op.drop_column("events", "created_at")
+9 -3
View File
@@ -647,7 +647,8 @@ async def admin_park_tenant_members(
d["user"] = {"id": m["user_id"], "username": "", "nickname": "", "phone": "",
"role": "", "avatar": "", "certification_status": "", "deleted": True}
# 查询该用户在本园区下是否为企业管理员
# 查询该用户在本园区下的所属公司及是否为企业管理员
d["companies"] = []
d["company_admin"] = None
try:
cm_rows = (await db.session.execute(
@@ -660,12 +661,17 @@ async def admin_park_tenant_members(
)
)).all()
for cm, pc in cm_rows:
if cm.member_type == "admin" or cm.is_admin:
is_admin = cm.member_type == "admin" or cm.is_admin
d["companies"].append({
"company_id": pc.id,
"company_name": pc.name,
"is_admin": is_admin,
})
if is_admin and d["company_admin"] is None:
d["company_admin"] = {
"company_id": pc.id,
"company_name": pc.name,
}
break
except Exception:
pass
+23
View File
@@ -263,6 +263,29 @@ async def opc_messages(
return {"items": await db.messages.list_by_user(user["id"])}
@router.get("/messages/unread-count", summary="站内消息未读数(红点角标)")
async def opc_messages_unread_count(
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member")),
):
return {"unread": await db.messages.unread_count(user["id"])}
@router.post("/messages/read", summary="标记站内消息已读")
async def opc_messages_read(
payload: dict,
db: Database = Depends(get_db),
user: dict = Depends(require_roles("opc_member")),
):
"""body 传 ``{"ids": [...]}`` 标记指定消息;不传 ids 标记全部已读。"""
raw_ids = payload.get("ids")
ids: list[str] | None = None
if raw_ids:
ids = [str(i) for i in raw_ids if str(i)]
updated = await db.messages.mark_read(user["id"], ids)
return {"ok": True, "updated": updated}
@router.get("/profile", summary="个人资料")
async def opc_profile(
db: Database = Depends(get_db),
+1
View File
@@ -976,6 +976,7 @@ class Event(Base):
signup_mode: Mapped[str | None] = mapped_column(String, default="mini") # mini 小程序报名 | external 外链报名
publisher_id: Mapped[str | None] = mapped_column(String, default="") # 发布者
publisher_name: Mapped[str | None] = mapped_column(String, default="")
created_at: Mapped[str | None] = mapped_column(String, default="") # 发布/创建时间(按此排序)
class Booking(Base):
+21
View File
@@ -2470,6 +2470,27 @@ class MessageRepository:
)).all()
)
async def mark_read(
self,
user_id: str,
message_ids: list[str] | None = None,
) -> int:
"""标记站内消息已读;message_ids 为空时标记该用户全部消息。
Returns:
本次更新为已读的消息条数。
"""
stmt = (
update(Message)
.where(Message.user_id == user_id, Message.read.is_(False))
)
if message_ids:
stmt = stmt.where(Message.id.in_(message_ids))
stmt = stmt.values(read=True)
result = await self.session.execute(stmt)
await self.session.commit()
return int(result.rowcount or 0)
@staticmethod
def _to_dict(m: Message) -> dict:
return {
+1 -1
View File
@@ -45,7 +45,7 @@ from app.api.routers import invite as invite_router
from app.api.routers import rbac_compute_pricing as compute_pricing_router
from app.im import router as im_router
APP_NAME = "超服Agents 演示后端"
APP_NAME = "南省超级个体服务平台"
@asynccontextmanager
+114
View File
@@ -0,0 +1,114 @@
-----BEGIN CERTIFICATE-----
MIIHFDCCBPygAwIBAgIULTMMj1JHpgAyKuYia2zWpYunbvcwDQYJKoZIhvcNAQEL
BQAwWzELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dp
ZXMsIEluYy4xJTAjBgNVBAMTHFRydXN0QXNpYSBEViBUTFMgUlNBIENBIDIwMjQw
HhcNMjYwODIyMDYwMDAwWhcNMjYxMTIwMDU1OTU5WjAbMRkwFwYDVQQDExBvcGMu
cGluZXNvdW5kLmNuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtQDR
H3LY/xELOkGOOIt0+e7INzifREex2xvO2TCv8lS5QsSW600W5KVJui5aqOUIa1FB
XnFx4UvMgLqt5Dup5maTudK1kPVx/lSdPSKYvFm6VmOLT0LbqwX/YAoufZi3ga/v
lNPnXlPVqBp57Yy5ocDjeqROYiApC29gS12VN3GuZW6FfMWZt3Sjjcqul3j0QcFf
gB0eM83p6NUMuzeLdY1lTfah0gNqWIbg6vjgpWRGoISU0egF8jXzHujtbCZjmGSa
wpvwCiGnP8urjRPWu8CnMuvnMM/awbeKxnCm6zbgd8jCnu0yBQ+s/dT1NY9a1aDF
bsda84oeh1Vor9JPYQIDAQABo4IDDjCCAwowDAYDVR0TAQH/BAIwADAfBgNVHSME
GDAWgBRhdQVOGvUPwWAgIMIo2Yuz4sF9tDCBlwYIKwYBBQUHAQEEgYowgYcwQwYI
KwYBBQUHMAKGN2h0dHA6Ly9pY2Eud3QudHJ1c3Rhc2lhLmNvbS9UcnVzdEFzaWFE
VlRMU1JTQUNBMjAyNC5jcnQwQAYIKwYBBQUHMAGGNGh0dHA6Ly9vY3NwLnd0LnRy
dXN0YXNpYS5jb20vVHJ1c3RBc2lhRFZUTFNSU0FDQTIwMjQwGwYDVR0RBBQwEoIQ
b3BjLnBpbmVzb3VuZC5jbjATBgNVHSAEDDAKMAgGBmeBDAECATATBgNVHSUEDDAK
BggrBgEFBQcDATBIBgNVHR8EQTA/MD2gO6A5hjdodHRwOi8vY3JsLnd0LnRydXN0
YXNpYS5jb20vVHJ1c3RBc2lhRFZUTFNSU0FDQTIwMjQuY3JsMB0GA1UdDgQWBBRa
rxx7jMxy7/4JhVLhBKkfEQuhYDAOBgNVHQ8BAf8EBAMCBaAwggF9BgorBgEEAdZ5
AgQCBIIBbQSCAWkBZwB1ANgJVTuUT3r/yBYZb5RPhauw+Pxeh1UmDxXRLnK7RUsU
AAABoChcLDQAAAQDAEYwRAIgRTNq73ITRCk2KkeepTCsgzP9hUMfRBi9AS5NayLZ
MzcCIEKfEFIa1r5ql7nvm+ssANLFJo/5HqWvS1E7SG7We760AHcAdNudWPfUfp39
eHoWKpkcGM9pjafHKZGMmhiwRQ26RLwAAAGgKFwl4AAABAMASDBGAiEAxzOsRwlC
Q3feu30eGnzyJniYRlHCSxulEJ4ioPwHNs0CIQDojAAu8n/ffYkVzTxdDsmR0CDG
U/ypjAuYbEj8Sfrd1wB1ANdtfRDRp/V3wsfpX9cAv/mCyTNaZeHQswFzF8DIxWl3
AAABoChcKRgAAAQDAEYwRAIgWumrxIXo1slMAeSOQjqfDMEQAH7KRjT3cPDH/tzA
fBQCICV9Ne43B14pro9okUfo7+F7/MpRWpB437jZUS+XRtH+MA0GCSqGSIb3DQEB
CwUAA4ICAQAmHEI6wsRNP4GkVXslVOOg0V1+gbSpqGk15OE9x2Vafg63MK3acci0
yFSbGboOZQOoidWLIiG3q04Z5mmgsvujphggYMBx2keUz4BrJ1N+ClRwn7an/vPI
oGOcbCTlNk9/k6odpoFR8lgvGNO/BwckWG4kVyVvhVf30gSDnZ02St+/C4MWP5zd
lHVmMeHSN/odbhCchTlpwRksYgHLj7RHkp0c/wksSdGSsygVER3UoID/TVOaU7rp
VuAuTkOb953tJz2Mbso2AqnpawFtu3aIh/+K6ZljpiyseSm+oG1UVxIJjQdVLbXE
l9GTM344PfcA62Q7spEFt/MIUaVPiObBFloAoWs9182eCEg0rnuUtXEUDMDfJZdv
lEzTaWgkpnagMMiNyPghQnfQ2dfCqkdcYm/MP2V7tmBH0WpJcZIgOB3E37e1zk3O
YSCAxpj7ltuFnmcGwEuBHh/HYlISl3dT2+NwO3AVxzAeb1Wgz6PGJh+kqPmU/1//
uMQqFOgg2kZ0nGo4938qMH0KmXS7ioHrhs6LMikXDxRyj/H7NjSPseSbVjBeBZA7
licFJdgwZtOsRfu3PM9QOatOdQgQh4sm2h8C85SraHGmnJoeAxU3Wv6uDgNNk/MR
Retkjz/P8l3C+HgCcrITU7vD+ZF5Djm9X2C2pspShcxUsEBrSeWHxA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGvTCCBKWgAwIBAgIUHJ5rQS3rCoVGHeA9vjCYTQVziIswDQYJKoZIhvcNAQEM
BQAwWDELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dp
ZXMsIEluYy4xIjAgBgNVBAMTGVRydXN0QXNpYSBUTFMgUlNBIFJvb3QgQ0EwHhcN
MjQwNTE1MDYxOTE1WhcNMzQwNTE1MDYxOTE0WjBbMQswCQYDVQQGEwJDTjElMCMG
A1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjElMCMGA1UEAxMcVHJ1
c3RBc2lhIERWIFRMUyBSU0EgQ0EgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAK2GCKULRuSjgEhVn7j0cnfuSmrxS5/5H77qmOJwWJkMVE+SgBQn
4mkoxVwF03E517T5YZZumwy5iCxktdnKp5Uq0C7td8yYnZgLb18pUeuzfkJBNWvI
MccfeGCI6qdnl1nGv15loYjP0TEt7C9VyVrK+zmeTxdkBb5xP/hQPFVRWQROnruX
gs7G1jbEX3Hhih+OR1dxcQkk7yVM493zuDVBBlNqH4KA/FawaGNcA/KRKHI0cqyT
Q/GjthO3yyivTlu07/QGV5VTl2oor9ntyXdCmEktu0aKH3kOLRjBE5CiT55Gts7d
1MBRwI3pAjLtDia4+Sz0XmPB/mH1/KO7EKtg+5XVZe3amDpboawWAS2oaKuDKWx/
fh5AA0ViSS+8nDAbrxE4b/gPno28GZSG41VELtSxZvgA0T8lc1oBBywQc0BWfCC9
PoSl2xSxhjKjJfY+od+EALfmEnEH3A53iK4z4gqpBo8O29omZ8HYxO9Tb5aemgla
gP+8oWmxv6aZwYOpWHb3AqOxEoTMV0VwzfEqLsMGcP6koLCB51uPGu8kDF8PIpbu
jallH/6K9QySf3S2H4SCme94tAjznz58En48PGoQPCSTDM6CyeEZqR3tEAnUF1gS
4T+hkEiskHWLQ6QosxoFX2+qMMeQNHictf5O/ZPXvIv34yxE5kiiBWN3AgMBAAGj
ggF6MIIBdjASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFLgHkXlcBvRG
/XtZylomkadFK/hTMIGTBggrBgEFBQcBAQSBhjCBgzBBBggrBgEFBQcwAoY1aHR0
cDovL2ljYS53dC50cnVzdGFzaWEuY29tL1RydXN0QXNpYVRMU1JTQVJvb3RDQS5j
cnQwPgYIKwYBBQUHMAGGMmh0dHA6Ly9vY3NwLnd0LnRydXN0YXNpYS5jb20vVHJ1
c3RBc2lhVExTUlNBUm9vdENBMBMGA1UdIAQMMAowCAYGZ4EMAQIBMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjBGBgNVHR8EPzA9MDugOaA3hjVodHRwOi8v
Y3JsLnd0LnRydXN0YXNpYS5jb20vVHJ1c3RBc2lhVExTUlNBUm9vdENBLmNybDAd
BgNVHQ4EFgQUYXUFThr1D8FgICDCKNmLs+LBfbQwDgYDVR0PAQH/BAQDAgGGMA0G
CSqGSIb3DQEBDAUAA4ICAQCpIjcVXxHv5IjZr2h2+PLiKzm8CW0EoSftEvjLDMec
Roc7okR/tz3WZ/NWS6YxURaE0Qbt0ZHLOtPisaACcpOCVeoLhHLfF54U2RQposA/
NK8Gdu1vNHWHDpF9H2MiY+VBPKr+lctYcvV/MipWa7SsBkZEu/Srd5Gp5tU4G7L0
a/X7xcv/lwjbdvARe0Rz5tp1BEn0P/7exFqN3gkqujo5RvMoegi0EgsuM+ya5bCu
vyfbp9Qzah+atfvzN+sYVaJjxnjogil7RRPNq+eR3nGocA+U0ypKl4ifpr+droZ9
Fk7axJ7Fs3kkIxomcSINLFv0+m2IFOQvUWn5OBXkeP+NcEaVySJx/TTpHJ7U+Ill
DzZZhAoqdxGb0Ac0SLmtGHnQVCs93aWIMcTNYqJtagE39kN+EcPAVb1znp8a7tw6
D6kI1cKTLBHMyDBtUAZVmboDacJmQodTQ+tFbIMCRzrP2M51CiXDK4L6W27C8b9V
Upq9WlIFeTfIqtkAq1iHyjv0YdoReprIHD2r/9g5X4mbMmsOFyaMT+lwnT5S0StD
+eiMQauCYnfqf88qJwbT5xCgRbd01yVm/V448GS/5XCZNSer4dToOzGcRvjBJTft
XbdVkfXe0MAP3xhbpoihEKPyorWPLWr/iWcVDq4o25yFTSe/WFa30ZwieLhMDign
Gg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF/TCCBOWgAwIBAgIRALLcTDSTLXCc1L8LCZQBaVwwDQYJKoZIhvcNAQEMBQAw
fjELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEiMCAG
A1UEAxMZQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQTAeFw0yNTEwMDcwNjU3MDha
Fw0yOTAxMTQwNjU3MDhaMFgxCzAJBgNVBAYTAkNOMSUwIwYDVQQKExxUcnVzdEFz
aWEgVGVjaG5vbG9naWVzLCBJbmMuMSIwIAYDVQQDExlUcnVzdEFzaWEgVExTIFJT
QSBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwxa4G2qk
RHPl1k70uc9bC8HRmoH1Y7CPQ8G7CFoaeuEHdiYfj2lWvv42YNAMg82U6ufFLVwv
BRYCngqoL+VgJlSW+JhAHayunXRnL1T7lGOY4yZKg8aVtglDUM0Nfd5EDmAST1s1
vb+ZOG19bNriaHMf+TGlGRBzBSrDMhmF6jSq3UIeMI0/nrUeYdVvvQBycq0SP6qm
SXPyhhWVDBBfUWTO/3e4yW2s5dWY8ZkubOPJRPm1QycITfZ+3kR5u7KMHNrTS2wu
1sN4t0zVpOTa3IqODv8Pw6ZgJijPNr/6VwrsWz/pMBZ7xNvE1GSgMPvl/R1xkt0p
j0FY3gCtOv09fBqosRfwTjR4WCXWhSHreR3Q3KvPYjywx5eL1tCf0/483sXj/DqD
cIQhHQnCofy7KCFlU2B6kG2WOFv/8dd6W23TyXBJSrodOtBQ2jIgGeSLP+vVFiY3
PPt1nrAHcLgUYHfc9g9cUgq8XW2NUQraxRjIm23csIOzf6NOeEyYJavyfi4gXoIV
ptbYj6zlzTKGyPnk2oniOz6TxRNq/8f3GPxnte8YLFSrQNNsTe3FyLc4T2TElExP
oM2upcYnJXUnbcbanI6wBZnVKBHFIdS4/MeWq0pkWWFrE/xLzJKmD6RRvw5YkNmC
gzmmX7P6bpKzzTMOhajxKdOrWa3W297UK4ECAwEAAaOCAZowggGWMHUGCCsGAQUF
BwEBBGkwZzA7BggrBgEFBQcwAoYvaHR0cDovL3RydXN0YXNpYS5yZXBvc2l0b3J5
LmNlcnR1bS5wbC9jdG5jYS5jZXIwKAYIKwYBBQUHMAGGHGh0dHA6Ly9zdWJjYS5v
Y3NwLWNlcnR1bS5jb20wHwYDVR0jBBgwFoAUCHbNywf/JPbFze27kLzihDdGdfcw
EgYDVR0TAQH/BAgwBgEB/wIBAjBtBgNVHR8EZjBkMDKgMKAuhixodHRwOi8vY2Vy
dHVtLWNybC53dC50cnVzdGFzaWEuY29tL2N0bmNhLmNybDAuoCygKoYoaHR0cDov
L3RydXN0YXNpYS5jcmwuY2VydHVtLnBsL2N0bmNhLmNybDATBgNVHSUEDDAKBggr
BgEFBQcDATAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0gBC4wLDAIBgZngQwBAgEwCAYG
Z4EMAQICMAcGBWeBDAEBMA0GCysGAQQBgttOAgEBMB0GA1UdDgQWBBS4B5F5XAb0
Rv17WcpaJpGnRSv4UzANBgkqhkiG9w0BAQwFAAOCAQEAIf2vyzSuFzECCTH3LPB/
bmUH1aXbVm8zVwxof+4tn8EsNYH8CgM2AHxRkXP5jOitqylySAuVIATJLYFmBQJO
XMCgf9NbbuG8P1VgnUQuUhQMChp2zT946Y55xZVANGg4o2+orw4SpclSy/R7eayX
Lccr++uqke9gJlX3FCpi4Sa8sg/kSfSv4p7tuwu4YAk+XPBew3jQc9wueAJwQ/Rz
H4LtICrekL0QuNHb0BhPcn4qELBF6f+IrOBA2MKw1m0C9y6uO8K+xd1JMpG0/RYZ
cGIH+rR5fBymQSNuD+F5I2PeXMPpigPcEdOi/NWZcCLncJLDrEU62T8hkif6MQ4F
Dw==
-----END CERTIFICATE-----